4 * base class for bootstrap elements.
8 Roo.bootstrap = Roo.bootstrap || {};
10 * @class Roo.bootstrap.Component
11 * @extends Roo.Component
12 * Bootstrap Component base class
13 * @cfg {String} cls css class
14 * @cfg {String} style any extra css
15 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
17 * @cfg {string} dataId cutomer id
18 * @cfg {string} name Specifies name attribute
19 * @cfg {string} tooltip Text for the tooltip
20 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
23 * Do not use directly - it does not do anything..
24 * @param {Object} config The config object
29 Roo.bootstrap.Component = function(config){
30 Roo.bootstrap.Component.superclass.constructor.call(this, config);
34 * @event childrenrendered
35 * Fires when the children have been rendered..
36 * @param {Roo.bootstrap.Component} this
38 "childrenrendered" : true
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
50 allowDomMove : false, // to stop relocations in parent onRender...
60 * Initialize Events for the element
62 initEvents : function() { },
68 can_build_overlaid : true,
70 container_method : false,
77 // returns the parent component..
78 return Roo.ComponentMgr.get(this.parentId)
84 onRender : function(ct, position)
86 // Roo.log("Call onRender: " + this.xtype);
88 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
91 if (this.el.attr('xtype')) {
92 this.el.attr('xtypex', this.el.attr('xtype'));
93 this.el.dom.removeAttribute('xtype');
103 var cfg = Roo.apply({}, this.getAutoCreate());
105 cfg.id = this.id || Roo.id();
107 // fill in the extra attributes
108 if (this.xattr && typeof(this.xattr) =='object') {
109 for (var i in this.xattr) {
110 cfg[i] = this.xattr[i];
115 cfg.dataId = this.dataId;
119 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
122 if (this.style) { // fixme needs to support more complex style data.
123 cfg.style = this.style;
127 cfg.name = this.name;
130 this.el = ct.createChild(cfg, position);
133 this.tooltipEl().attr('tooltip', this.tooltip);
136 if(this.tabIndex !== undefined){
137 this.el.dom.setAttribute('tabIndex', this.tabIndex);
144 * Fetch the element to add children to
145 * @return {Roo.Element} defaults to this.el
147 getChildContainer : function()
152 * Fetch the element to display the tooltip on.
153 * @return {Roo.Element} defaults to this.el
155 tooltipEl : function()
160 addxtype : function(tree,cntr)
164 cn = Roo.factory(tree);
165 //Roo.log(['addxtype', cn]);
167 cn.parentType = this.xtype; //??
168 cn.parentId = this.id;
170 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
171 if (typeof(cn.container_method) == 'string') {
172 cntr = cn.container_method;
176 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
178 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
180 var build_from_html = Roo.XComponent.build_from_html;
182 var is_body = (tree.xtype == 'Body') ;
184 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
186 var self_cntr_el = Roo.get(this[cntr](false));
188 // do not try and build conditional elements
189 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
193 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
194 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
195 return this.addxtypeChild(tree,cntr, is_body);
198 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
201 return this.addxtypeChild(Roo.apply({}, tree),cntr);
204 Roo.log('skipping render');
210 if (!build_from_html) {
214 // this i think handles overlaying multiple children of the same type
215 // with the sam eelement.. - which might be buggy..
217 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
223 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
227 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
234 addxtypeChild : function (tree, cntr, is_body)
236 Roo.debug && Roo.log('addxtypeChild:' + cntr);
238 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
241 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
242 (typeof(tree['flexy:foreach']) != 'undefined');
246 skip_children = false;
247 // render the element if it's not BODY.
250 cn = Roo.factory(tree);
252 cn.parentType = this.xtype; //??
253 cn.parentId = this.id;
255 var build_from_html = Roo.XComponent.build_from_html;
258 // does the container contain child eleemnts with 'xtype' attributes.
259 // that match this xtype..
260 // note - when we render we create these as well..
261 // so we should check to see if body has xtype set.
262 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
264 var self_cntr_el = Roo.get(this[cntr](false));
265 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
267 //Roo.log(Roo.XComponent.build_from_html);
268 //Roo.log("got echild:");
271 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
272 // and are not displayed -this causes this to use up the wrong element when matching.
273 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
276 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
277 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
283 //echild.dom.removeAttribute('xtype');
285 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
286 Roo.debug && Roo.log(self_cntr_el);
287 Roo.debug && Roo.log(echild);
288 Roo.debug && Roo.log(cn);
294 // if object has flexy:if - then it may or may not be rendered.
295 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
296 // skip a flexy if element.
297 Roo.debug && Roo.log('skipping render');
298 Roo.debug && Roo.log(tree);
300 Roo.debug && Roo.log('skipping all children');
301 skip_children = true;
306 // actually if flexy:foreach is found, we really want to create
307 // multiple copies here...
309 //Roo.log(this[cntr]());
310 // some elements do not have render methods.. like the layouts...
311 cn.render && cn.render(this[cntr](true));
313 // then add the element..
321 if (typeof (tree.menu) != 'undefined') {
322 tree.menu.parentType = cn.xtype;
323 tree.menu.triggerEl = cn.el;
324 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
328 if (!tree.items || !tree.items.length) {
330 //Roo.log(["no children", this]);
335 var items = tree.items;
338 //Roo.log(items.length);
340 if (!skip_children) {
341 for(var i =0;i < items.length;i++) {
342 // Roo.log(['add child', items[i]]);
343 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
349 //Roo.log("fire childrenrendered");
351 cn.fireEvent('childrenrendered', this);
356 * Show a component - removes 'hidden' class
361 this.el.removeClass('hidden');
365 * Hide a component - adds 'hidden' class
369 if (this.el && !this.el.hasClass('hidden')) {
370 this.el.addClass('hidden');
384 * @class Roo.bootstrap.Body
385 * @extends Roo.bootstrap.Component
386 * Bootstrap Body class
390 * @param {Object} config The config object
393 Roo.bootstrap.Body = function(config){
395 config = config || {};
397 Roo.bootstrap.Body.superclass.constructor.call(this, config);
398 this.el = Roo.get(config.el ? config.el : document.body );
399 if (this.cls && this.cls.length) {
400 Roo.get(document.body).addClass(this.cls);
404 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
406 is_body : true,// just to make sure it's constructed?
411 onRender : function(ct, position)
413 /* Roo.log("Roo.bootstrap.Body - onRender");
414 if (this.cls && this.cls.length) {
415 Roo.get(document.body).addClass(this.cls);
434 * @class Roo.bootstrap.ButtonGroup
435 * @extends Roo.bootstrap.Component
436 * Bootstrap ButtonGroup class
437 * @cfg {String} size lg | sm | xs (default empty normal)
438 * @cfg {String} align vertical | justified (default none)
439 * @cfg {String} direction up | down (default down)
440 * @cfg {Boolean} toolbar false | true
441 * @cfg {Boolean} btn true | false
446 * @param {Object} config The config object
449 Roo.bootstrap.ButtonGroup = function(config){
450 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
453 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
461 getAutoCreate : function(){
467 cfg.html = this.html || cfg.html;
478 if (['vertical','justified'].indexOf(this.align)!==-1) {
479 cfg.cls = 'btn-group-' + this.align;
481 if (this.align == 'justified') {
482 console.log(this.items);
486 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
487 cfg.cls += ' btn-group-' + this.size;
490 if (this.direction == 'up') {
491 cfg.cls += ' dropup' ;
507 * @class Roo.bootstrap.Button
508 * @extends Roo.bootstrap.Component
509 * Bootstrap Button class
510 * @cfg {String} html The button content
511 * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default
512 * @cfg {String} size ( lg | sm | xs)
513 * @cfg {String} tag ( a | input | submit)
514 * @cfg {String} href empty or href
515 * @cfg {Boolean} disabled default false;
516 * @cfg {Boolean} isClose default false;
517 * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
518 * @cfg {String} badge text for badge
519 * @cfg {String} theme default
520 * @cfg {Boolean} inverse
521 * @cfg {Boolean} toggle
522 * @cfg {String} ontext text for on toggle state
523 * @cfg {String} offtext text for off toggle state
524 * @cfg {Boolean} defaulton
525 * @cfg {Boolean} preventDefault default true
526 * @cfg {Boolean} removeClass remove the standard class..
527 * @cfg {String} target target for a href. (_self|_blank|_parent|_top| other)
530 * Create a new button
531 * @param {Object} config The config object
535 Roo.bootstrap.Button = function(config){
536 Roo.bootstrap.Button.superclass.constructor.call(this, config);
541 * When a butotn is pressed
542 * @param {Roo.bootstrap.Button} this
543 * @param {Roo.EventObject} e
548 * After the button has been toggles
549 * @param {Roo.EventObject} e
550 * @param {boolean} pressed (also available as button.pressed)
556 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
574 preventDefault: true,
583 getAutoCreate : function(){
591 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
592 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
597 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
599 if (this.toggle == true) {
602 cls: 'slider-frame roo-button',
607 'data-off-text':'OFF',
608 cls: 'slider-button',
614 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
615 cfg.cls += ' '+this.weight;
624 cfg["aria-hidden"] = true;
626 cfg.html = "×";
632 if (this.theme==='default') {
633 cfg.cls = 'btn roo-button';
635 //if (this.parentType != 'Navbar') {
636 this.weight = this.weight.length ? this.weight : 'default';
638 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
640 cfg.cls += ' btn-' + this.weight;
642 } else if (this.theme==='glow') {
645 cfg.cls = 'btn-glow roo-button';
647 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
649 cfg.cls += ' ' + this.weight;
655 this.cls += ' inverse';
660 cfg.cls += ' active';
664 cfg.disabled = 'disabled';
668 Roo.log('changing to ul' );
670 this.glyphicon = 'caret';
673 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
675 //gsRoo.log(this.parentType);
676 if (this.parentType === 'Navbar' && !this.parent().bar) {
677 Roo.log('changing to li?');
686 href : this.href || '#'
689 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
690 cfg.cls += ' dropdown';
697 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
699 if (this.glyphicon) {
700 cfg.html = ' ' + cfg.html;
705 cls: 'glyphicon glyphicon-' + this.glyphicon
715 // cfg.cls='btn roo-button';
719 var value = cfg.html;
724 cls: 'glyphicon glyphicon-' + this.glyphicon,
743 cfg.cls += ' dropdown';
744 cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
747 if (cfg.tag !== 'a' && this.href !== '') {
748 throw "Tag must be a to set href.";
749 } else if (this.href.length > 0) {
750 cfg.href = this.href;
753 if(this.removeClass){
758 cfg.target = this.target;
763 initEvents: function() {
764 // Roo.log('init events?');
765 // Roo.log(this.el.dom);
768 if (typeof (this.menu) != 'undefined') {
769 this.menu.parentType = this.xtype;
770 this.menu.triggerEl = this.el;
771 this.addxtype(Roo.apply({}, this.menu));
775 if (this.el.hasClass('roo-button')) {
776 this.el.on('click', this.onClick, this);
778 this.el.select('.roo-button').on('click', this.onClick, this);
781 if(this.removeClass){
782 this.el.on('click', this.onClick, this);
785 this.el.enableDisplayMode();
788 onClick : function(e)
795 Roo.log('button on click ');
796 if(this.preventDefault){
799 if (this.pressed === true || this.pressed === false) {
800 this.pressed = !this.pressed;
801 this.el[this.pressed ? 'addClass' : 'removeClass']('active');
802 this.fireEvent('toggle', this, e, this.pressed);
806 this.fireEvent('click', this, e);
810 * Enables this button
814 this.disabled = false;
815 this.el.removeClass('disabled');
819 * Disable this button
823 this.disabled = true;
824 this.el.addClass('disabled');
827 * sets the active state on/off,
828 * @param {Boolean} state (optional) Force a particular state
830 setActive : function(v) {
832 this.el[v ? 'addClass' : 'removeClass']('active');
835 * toggles the current active state
837 toggleActive : function()
839 var active = this.el.hasClass('active');
840 this.setActive(!active);
844 setText : function(str)
846 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
850 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
873 * @class Roo.bootstrap.Column
874 * @extends Roo.bootstrap.Component
875 * Bootstrap Column class
876 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
877 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
878 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
879 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
880 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
881 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
882 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
883 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
886 * @cfg {Boolean} hidden (true|false) hide the element
887 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
888 * @cfg {String} fa (ban|check|...) font awesome icon
889 * @cfg {Number} fasize (1|2|....) font awsome size
891 * @cfg {String} icon (info-sign|check|...) glyphicon name
893 * @cfg {String} html content of column.
896 * Create a new Column
897 * @param {Object} config The config object
900 Roo.bootstrap.Column = function(config){
901 Roo.bootstrap.Column.superclass.constructor.call(this, config);
904 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
922 getAutoCreate : function(){
923 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
931 ['xs','sm','md','lg'].map(function(size){
932 //Roo.log( size + ':' + settings[size]);
934 if (settings[size+'off'] !== false) {
935 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
938 if (settings[size] === false) {
942 if (!settings[size]) { // 0 = hidden
943 cfg.cls += ' hidden-' + size;
946 cfg.cls += ' col-' + size + '-' + settings[size];
951 cfg.cls += ' hidden';
954 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
955 cfg.cls +=' alert alert-' + this.alert;
959 if (this.html.length) {
960 cfg.html = this.html;
964 if (this.fasize > 1) {
965 fasize = ' fa-' + this.fasize + 'x';
967 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
972 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
991 * @class Roo.bootstrap.Container
992 * @extends Roo.bootstrap.Component
993 * Bootstrap Container class
994 * @cfg {Boolean} jumbotron is it a jumbotron element
995 * @cfg {String} html content of element
996 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
997 * @cfg {String} panel (primary|success|info|warning|danger) render as panel - type - primary/success.....
998 * @cfg {String} header content of header (for panel)
999 * @cfg {String} footer content of footer (for panel)
1000 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1001 * @cfg {String} tag (header|aside|section) type of HTML tag.
1002 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1003 * @cfg {String} fa font awesome icon
1004 * @cfg {String} icon (info-sign|check|...) glyphicon name
1005 * @cfg {Boolean} hidden (true|false) hide the element
1006 * @cfg {Boolean} expandable (true|false) default false
1007 * @cfg {Boolean} expanded (true|false) default true
1008 * @cfg {String} rheader contet on the right of header
1009 * @cfg {Boolean} clickable (true|false) default false
1013 * Create a new Container
1014 * @param {Object} config The config object
1017 Roo.bootstrap.Container = function(config){
1018 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1024 * After the panel has been expand
1026 * @param {Roo.bootstrap.Container} this
1031 * After the panel has been collapsed
1033 * @param {Roo.bootstrap.Container} this
1038 * When a element is chick
1039 * @param {Roo.bootstrap.Container} this
1040 * @param {Roo.EventObject} e
1046 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1064 getChildContainer : function() {
1070 if (this.panel.length) {
1071 return this.el.select('.panel-body',true).first();
1078 getAutoCreate : function(){
1081 tag : this.tag || 'div',
1085 if (this.jumbotron) {
1086 cfg.cls = 'jumbotron';
1091 // - this is applied by the parent..
1093 // cfg.cls = this.cls + '';
1096 if (this.sticky.length) {
1098 var bd = Roo.get(document.body);
1099 if (!bd.hasClass('bootstrap-sticky')) {
1100 bd.addClass('bootstrap-sticky');
1101 Roo.select('html',true).setStyle('height', '100%');
1104 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1108 if (this.well.length) {
1109 switch (this.well) {
1112 cfg.cls +=' well well-' +this.well;
1121 cfg.cls += ' hidden';
1125 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1126 cfg.cls +=' alert alert-' + this.alert;
1131 if (this.panel.length) {
1132 cfg.cls += ' panel panel-' + this.panel;
1134 if (this.header.length) {
1138 if(this.expandable){
1140 cfg.cls = cfg.cls + ' expandable';
1144 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1152 cls : 'panel-title',
1153 html : (this.expandable ? ' ' : '') + this.header
1157 cls: 'panel-header-right',
1163 cls : 'panel-heading',
1164 style : this.expandable ? 'cursor: pointer' : '',
1172 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1177 if (this.footer.length) {
1179 cls : 'panel-footer',
1188 body.html = this.html || cfg.html;
1189 // prefix with the icons..
1191 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1194 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1199 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1200 cfg.cls = 'container';
1206 initEvents: function()
1208 if(this.expandable){
1209 var headerEl = this.headerEl();
1212 headerEl.on('click', this.onToggleClick, this);
1217 this.el.on('click', this.onClick, this);
1222 onToggleClick : function()
1224 var headerEl = this.headerEl();
1240 if(this.fireEvent('expand', this)) {
1242 this.expanded = true;
1244 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1246 this.el.select('.panel-body',true).first().removeClass('hide');
1248 var toggleEl = this.toggleEl();
1254 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1259 collapse : function()
1261 if(this.fireEvent('collapse', this)) {
1263 this.expanded = false;
1265 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1266 this.el.select('.panel-body',true).first().addClass('hide');
1268 var toggleEl = this.toggleEl();
1274 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1278 toggleEl : function()
1280 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1284 return this.el.select('.panel-heading .fa',true).first();
1287 headerEl : function()
1289 if(!this.el || !this.panel.length || !this.header.length){
1293 return this.el.select('.panel-heading',true).first()
1296 titleEl : function()
1298 if(!this.el || !this.panel.length || !this.header.length){
1302 return this.el.select('.panel-title',true).first();
1305 setTitle : function(v)
1307 var titleEl = this.titleEl();
1313 titleEl.dom.innerHTML = v;
1316 getTitle : function()
1319 var titleEl = this.titleEl();
1325 return titleEl.dom.innerHTML;
1328 setRightTitle : function(v)
1330 var t = this.el.select('.panel-header-right',true).first();
1336 t.dom.innerHTML = v;
1339 onClick : function(e)
1343 this.fireEvent('click', this, e);
1357 * @class Roo.bootstrap.Img
1358 * @extends Roo.bootstrap.Component
1359 * Bootstrap Img class
1360 * @cfg {Boolean} imgResponsive false | true
1361 * @cfg {String} border rounded | circle | thumbnail
1362 * @cfg {String} src image source
1363 * @cfg {String} alt image alternative text
1364 * @cfg {String} href a tag href
1365 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1366 * @cfg {String} xsUrl xs image source
1367 * @cfg {String} smUrl sm image source
1368 * @cfg {String} mdUrl md image source
1369 * @cfg {String} lgUrl lg image source
1372 * Create a new Input
1373 * @param {Object} config The config object
1376 Roo.bootstrap.Img = function(config){
1377 Roo.bootstrap.Img.superclass.constructor.call(this, config);
1383 * The img click event for the img.
1384 * @param {Roo.EventObject} e
1390 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1392 imgResponsive: true,
1402 getAutoCreate : function()
1404 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1405 return this.createSingleImg();
1410 cls: 'roo-image-responsive-group',
1415 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1417 if(!_this[size + 'Url']){
1423 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1424 html: _this.html || cfg.html,
1425 src: _this[size + 'Url']
1428 img.cls += ' roo-image-responsive-' + size;
1430 var s = ['xs', 'sm', 'md', 'lg'];
1432 s.splice(s.indexOf(size), 1);
1434 Roo.each(s, function(ss){
1435 img.cls += ' hidden-' + ss;
1438 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1439 cfg.cls += ' img-' + _this.border;
1443 cfg.alt = _this.alt;
1456 a.target = _this.target;
1460 cfg.cn.push((_this.href) ? a : img);
1467 createSingleImg : function()
1471 cls: (this.imgResponsive) ? 'img-responsive' : '',
1473 src : 'about:blank' // just incase src get's set to undefined?!?
1476 cfg.html = this.html || cfg.html;
1478 cfg.src = this.src || cfg.src;
1480 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1481 cfg.cls += ' img-' + this.border;
1498 a.target = this.target;
1503 return (this.href) ? a : cfg;
1506 initEvents: function()
1509 this.el.on('click', this.onClick, this);
1514 onClick : function(e)
1516 Roo.log('img onclick');
1517 this.fireEvent('click', this, e);
1520 * Sets the url of the image - used to update it
1521 * @param {String} url the url of the image
1524 setSrc : function(url)
1528 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1529 this.el.dom.src = url;
1533 this.el.select('img', true).first().dom.src = url;
1549 * @class Roo.bootstrap.Link
1550 * @extends Roo.bootstrap.Component
1551 * Bootstrap Link Class
1552 * @cfg {String} alt image alternative text
1553 * @cfg {String} href a tag href
1554 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1555 * @cfg {String} html the content of the link.
1556 * @cfg {String} anchor name for the anchor link
1557 * @cfg {String} fa - favicon
1559 * @cfg {Boolean} preventDefault (true | false) default false
1563 * Create a new Input
1564 * @param {Object} config The config object
1567 Roo.bootstrap.Link = function(config){
1568 Roo.bootstrap.Link.superclass.constructor.call(this, config);
1574 * The img click event for the img.
1575 * @param {Roo.EventObject} e
1581 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
1585 preventDefault: false,
1591 getAutoCreate : function()
1593 var html = this.html || '';
1595 if (this.fa !== false) {
1596 html = '<i class="fa fa-' + this.fa + '"></i>';
1601 // anchor's do not require html/href...
1602 if (this.anchor === false) {
1604 cfg.href = this.href || '#';
1606 cfg.name = this.anchor;
1607 if (this.html !== false || this.fa !== false) {
1610 if (this.href !== false) {
1611 cfg.href = this.href;
1615 if(this.alt !== false){
1620 if(this.target !== false) {
1621 cfg.target = this.target;
1627 initEvents: function() {
1629 if(!this.href || this.preventDefault){
1630 this.el.on('click', this.onClick, this);
1634 onClick : function(e)
1636 if(this.preventDefault){
1639 //Roo.log('img onclick');
1640 this.fireEvent('click', this, e);
1653 * @class Roo.bootstrap.Header
1654 * @extends Roo.bootstrap.Component
1655 * Bootstrap Header class
1656 * @cfg {String} html content of header
1657 * @cfg {Number} level (1|2|3|4|5|6) default 1
1660 * Create a new Header
1661 * @param {Object} config The config object
1665 Roo.bootstrap.Header = function(config){
1666 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1669 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1677 getAutoCreate : function(){
1682 tag: 'h' + (1 *this.level),
1683 html: this.html || ''
1695 * Ext JS Library 1.1.1
1696 * Copyright(c) 2006-2007, Ext JS, LLC.
1698 * Originally Released Under LGPL - original licence link has changed is not relivant.
1701 * <script type="text/javascript">
1705 * @class Roo.bootstrap.MenuMgr
1706 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1709 Roo.bootstrap.MenuMgr = function(){
1710 var menus, active, groups = {}, attached = false, lastShow = new Date();
1712 // private - called when first menu is created
1715 active = new Roo.util.MixedCollection();
1716 Roo.get(document).addKeyListener(27, function(){
1717 if(active.length > 0){
1725 if(active && active.length > 0){
1726 var c = active.clone();
1736 if(active.length < 1){
1737 Roo.get(document).un("mouseup", onMouseDown);
1745 var last = active.last();
1746 lastShow = new Date();
1749 Roo.get(document).on("mouseup", onMouseDown);
1754 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1755 m.parentMenu.activeChild = m;
1756 }else if(last && last.isVisible()){
1757 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1762 function onBeforeHide(m){
1764 m.activeChild.hide();
1766 if(m.autoHideTimer){
1767 clearTimeout(m.autoHideTimer);
1768 delete m.autoHideTimer;
1773 function onBeforeShow(m){
1774 var pm = m.parentMenu;
1775 if(!pm && !m.allowOtherMenus){
1777 }else if(pm && pm.activeChild && active != m){
1778 pm.activeChild.hide();
1782 // private this should really trigger on mouseup..
1783 function onMouseDown(e){
1784 Roo.log("on Mouse Up");
1786 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1787 Roo.log("MenuManager hideAll");
1796 function onBeforeCheck(mi, state){
1798 var g = groups[mi.group];
1799 for(var i = 0, l = g.length; i < l; i++){
1801 g[i].setChecked(false);
1810 * Hides all menus that are currently visible
1812 hideAll : function(){
1817 register : function(menu){
1821 menus[menu.id] = menu;
1822 menu.on("beforehide", onBeforeHide);
1823 menu.on("hide", onHide);
1824 menu.on("beforeshow", onBeforeShow);
1825 menu.on("show", onShow);
1827 if(g && menu.events["checkchange"]){
1831 groups[g].push(menu);
1832 menu.on("checkchange", onCheck);
1837 * Returns a {@link Roo.menu.Menu} object
1838 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1839 * be used to generate and return a new Menu instance.
1841 get : function(menu){
1842 if(typeof menu == "string"){ // menu id
1844 }else if(menu.events){ // menu instance
1847 /*else if(typeof menu.length == 'number'){ // array of menu items?
1848 return new Roo.bootstrap.Menu({items:menu});
1849 }else{ // otherwise, must be a config
1850 return new Roo.bootstrap.Menu(menu);
1857 unregister : function(menu){
1858 delete menus[menu.id];
1859 menu.un("beforehide", onBeforeHide);
1860 menu.un("hide", onHide);
1861 menu.un("beforeshow", onBeforeShow);
1862 menu.un("show", onShow);
1864 if(g && menu.events["checkchange"]){
1865 groups[g].remove(menu);
1866 menu.un("checkchange", onCheck);
1871 registerCheckable : function(menuItem){
1872 var g = menuItem.group;
1877 groups[g].push(menuItem);
1878 menuItem.on("beforecheckchange", onBeforeCheck);
1883 unregisterCheckable : function(menuItem){
1884 var g = menuItem.group;
1886 groups[g].remove(menuItem);
1887 menuItem.un("beforecheckchange", onBeforeCheck);
1899 * @class Roo.bootstrap.Menu
1900 * @extends Roo.bootstrap.Component
1901 * Bootstrap Menu class - container for MenuItems
1902 * @cfg {String} type (dropdown|treeview|submenu) type of menu
1903 * @cfg {bool} hidden if the menu should be hidden when rendered.
1904 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
1905 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
1909 * @param {Object} config The config object
1913 Roo.bootstrap.Menu = function(config){
1914 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1915 if (this.registerMenu && this.type != 'treeview') {
1916 Roo.bootstrap.MenuMgr.register(this);
1921 * Fires before this menu is displayed
1922 * @param {Roo.menu.Menu} this
1927 * Fires before this menu is hidden
1928 * @param {Roo.menu.Menu} this
1933 * Fires after this menu is displayed
1934 * @param {Roo.menu.Menu} this
1939 * Fires after this menu is hidden
1940 * @param {Roo.menu.Menu} this
1945 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1946 * @param {Roo.menu.Menu} this
1947 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1948 * @param {Roo.EventObject} e
1953 * Fires when the mouse is hovering over this menu
1954 * @param {Roo.menu.Menu} this
1955 * @param {Roo.EventObject} e
1956 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1961 * Fires when the mouse exits this menu
1962 * @param {Roo.menu.Menu} this
1963 * @param {Roo.EventObject} e
1964 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1969 * Fires when a menu item contained in this menu is clicked
1970 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1971 * @param {Roo.EventObject} e
1975 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1978 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
1982 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
1985 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1987 registerMenu : true,
1989 menuItems :false, // stores the menu items..
1999 getChildContainer : function() {
2003 getAutoCreate : function(){
2005 //if (['right'].indexOf(this.align)!==-1) {
2006 // cfg.cn[1].cls += ' pull-right'
2012 cls : 'dropdown-menu' ,
2013 style : 'z-index:1000'
2017 if (this.type === 'submenu') {
2018 cfg.cls = 'submenu active';
2020 if (this.type === 'treeview') {
2021 cfg.cls = 'treeview-menu';
2026 initEvents : function() {
2028 // Roo.log("ADD event");
2029 // Roo.log(this.triggerEl.dom);
2031 this.triggerEl.on('click', this.onTriggerClick, this);
2033 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2035 this.triggerEl.addClass('dropdown-toggle');
2038 this.el.on('touchstart' , this.onTouch, this);
2040 this.el.on('click' , this.onClick, this);
2042 this.el.on("mouseover", this.onMouseOver, this);
2043 this.el.on("mouseout", this.onMouseOut, this);
2047 findTargetItem : function(e)
2049 var t = e.getTarget(".dropdown-menu-item", this.el, true);
2053 //Roo.log(t); Roo.log(t.id);
2055 //Roo.log(this.menuitems);
2056 return this.menuitems.get(t.id);
2058 //return this.items.get(t.menuItemId);
2064 onTouch : function(e)
2066 Roo.log("menu.onTouch");
2067 //e.stopEvent(); this make the user popdown broken
2071 onClick : function(e)
2073 Roo.log("menu.onClick");
2075 var t = this.findTargetItem(e);
2076 if(!t || t.isContainer){
2081 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
2082 if(t == this.activeItem && t.shouldDeactivate(e)){
2083 this.activeItem.deactivate();
2084 delete this.activeItem;
2088 this.setActiveItem(t, true);
2096 Roo.log('pass click event');
2100 this.fireEvent("click", this, t, e);
2104 (function() { _this.hide(); }).defer(100);
2107 onMouseOver : function(e){
2108 var t = this.findTargetItem(e);
2111 // if(t.canActivate && !t.disabled){
2112 // this.setActiveItem(t, true);
2116 this.fireEvent("mouseover", this, e, t);
2118 isVisible : function(){
2119 return !this.hidden;
2121 onMouseOut : function(e){
2122 var t = this.findTargetItem(e);
2125 // if(t == this.activeItem && t.shouldDeactivate(e)){
2126 // this.activeItem.deactivate();
2127 // delete this.activeItem;
2130 this.fireEvent("mouseout", this, e, t);
2135 * Displays this menu relative to another element
2136 * @param {String/HTMLElement/Roo.Element} element The element to align to
2137 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2138 * the element (defaults to this.defaultAlign)
2139 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2141 show : function(el, pos, parentMenu){
2142 this.parentMenu = parentMenu;
2146 this.fireEvent("beforeshow", this);
2147 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2150 * Displays this menu at a specific xy position
2151 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2152 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2154 showAt : function(xy, parentMenu, /* private: */_e){
2155 this.parentMenu = parentMenu;
2160 this.fireEvent("beforeshow", this);
2161 //xy = this.el.adjustForConstraints(xy);
2165 this.hideMenuItems();
2166 this.hidden = false;
2167 this.triggerEl.addClass('open');
2169 if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2170 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2173 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2178 this.fireEvent("show", this);
2184 this.doFocus.defer(50, this);
2188 doFocus : function(){
2190 this.focusEl.focus();
2195 * Hides this menu and optionally all parent menus
2196 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2198 hide : function(deep)
2201 this.hideMenuItems();
2202 if(this.el && this.isVisible()){
2203 this.fireEvent("beforehide", this);
2204 if(this.activeItem){
2205 this.activeItem.deactivate();
2206 this.activeItem = null;
2208 this.triggerEl.removeClass('open');;
2210 this.fireEvent("hide", this);
2212 if(deep === true && this.parentMenu){
2213 this.parentMenu.hide(true);
2217 onTriggerClick : function(e)
2219 Roo.log('trigger click');
2221 var target = e.getTarget();
2223 Roo.log(target.nodeName.toLowerCase());
2225 if(target.nodeName.toLowerCase() === 'i'){
2231 onTriggerPress : function(e)
2233 Roo.log('trigger press');
2234 //Roo.log(e.getTarget());
2235 // Roo.log(this.triggerEl.dom);
2237 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2238 var pel = Roo.get(e.getTarget());
2239 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2240 Roo.log('is treeview or dropdown?');
2244 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2248 if (this.isVisible()) {
2253 this.show(this.triggerEl, false, false);
2256 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2263 hideMenuItems : function()
2265 Roo.log("hide Menu Items");
2269 //$(backdrop).remove()
2270 this.el.select('.open',true).each(function(aa) {
2272 aa.removeClass('open');
2273 //var parent = getParent($(this))
2274 //var relatedTarget = { relatedTarget: this }
2276 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2277 //if (e.isDefaultPrevented()) return
2278 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2281 addxtypeChild : function (tree, cntr) {
2282 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2284 this.menuitems.add(comp);
2305 * @class Roo.bootstrap.MenuItem
2306 * @extends Roo.bootstrap.Component
2307 * Bootstrap MenuItem class
2308 * @cfg {String} html the menu label
2309 * @cfg {String} href the link
2310 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2311 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2312 * @cfg {Boolean} active used on sidebars to highlight active itesm
2313 * @cfg {String} fa favicon to show on left of menu item.
2314 * @cfg {Roo.bootsrap.Menu} menu the child menu.
2318 * Create a new MenuItem
2319 * @param {Object} config The config object
2323 Roo.bootstrap.MenuItem = function(config){
2324 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2329 * The raw click event for the entire grid.
2330 * @param {Roo.bootstrap.MenuItem} this
2331 * @param {Roo.EventObject} e
2337 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2341 preventDefault: false,
2342 isContainer : false,
2346 getAutoCreate : function(){
2348 if(this.isContainer){
2351 cls: 'dropdown-menu-item'
2365 if (this.fa !== false) {
2368 cls : 'fa fa-' + this.fa
2377 cls: 'dropdown-menu-item',
2380 if (this.parent().type == 'treeview') {
2381 cfg.cls = 'treeview-menu';
2384 cfg.cls += ' active';
2389 anc.href = this.href || cfg.cn[0].href ;
2390 ctag.html = this.html || cfg.cn[0].html ;
2394 initEvents: function()
2396 if (this.parent().type == 'treeview') {
2397 this.el.select('a').on('click', this.onClick, this);
2400 this.menu.parentType = this.xtype;
2401 this.menu.triggerEl = this.el;
2402 this.menu = this.addxtype(Roo.apply({}, this.menu));
2406 onClick : function(e)
2408 Roo.log('item on click ');
2410 if(this.preventDefault){
2413 //this.parent().hideMenuItems();
2415 this.fireEvent('click', this, e);
2434 * @class Roo.bootstrap.MenuSeparator
2435 * @extends Roo.bootstrap.Component
2436 * Bootstrap MenuSeparator class
2439 * Create a new MenuItem
2440 * @param {Object} config The config object
2444 Roo.bootstrap.MenuSeparator = function(config){
2445 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2448 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2450 getAutoCreate : function(){
2469 * @class Roo.bootstrap.Modal
2470 * @extends Roo.bootstrap.Component
2471 * Bootstrap Modal class
2472 * @cfg {String} title Title of dialog
2473 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2474 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2475 * @cfg {Boolean} specificTitle default false
2476 * @cfg {Array} buttons Array of buttons or standard button set..
2477 * @cfg {String} buttonPosition (left|right|center) default right
2478 * @cfg {Boolean} animate default true
2479 * @cfg {Boolean} allow_close default true
2480 * @cfg {Boolean} fitwindow default false
2481 * @cfg {String} size (sm|lg) default empty
2485 * Create a new Modal Dialog
2486 * @param {Object} config The config object
2489 Roo.bootstrap.Modal = function(config){
2490 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2495 * The raw btnclick event for the button
2496 * @param {Roo.EventObject} e
2500 this.buttons = this.buttons || [];
2503 this.tmpl = Roo.factory(this.tmpl);
2508 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2510 title : 'test dialog',
2520 specificTitle: false,
2522 buttonPosition: 'right',
2541 onRender : function(ct, position)
2543 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2546 var cfg = Roo.apply({}, this.getAutoCreate());
2549 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2551 //if (!cfg.name.length) {
2555 cfg.cls += ' ' + this.cls;
2558 cfg.style = this.style;
2560 this.el = Roo.get(document.body).createChild(cfg, position);
2562 //var type = this.el.dom.type;
2565 if(this.tabIndex !== undefined){
2566 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2569 this.dialogEl = this.el.select('.modal-dialog',true).first();
2570 this.bodyEl = this.el.select('.modal-body',true).first();
2571 this.closeEl = this.el.select('.modal-header .close', true).first();
2572 this.footerEl = this.el.select('.modal-footer',true).first();
2573 this.titleEl = this.el.select('.modal-title',true).first();
2577 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2578 this.maskEl.enableDisplayMode("block");
2580 //this.el.addClass("x-dlg-modal");
2582 if (this.buttons.length) {
2583 Roo.each(this.buttons, function(bb) {
2584 var b = Roo.apply({}, bb);
2585 b.xns = b.xns || Roo.bootstrap;
2586 b.xtype = b.xtype || 'Button';
2587 if (typeof(b.listeners) == 'undefined') {
2588 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2591 var btn = Roo.factory(b);
2593 btn.render(this.el.select('.modal-footer div').first());
2597 // render the children.
2600 if(typeof(this.items) != 'undefined'){
2601 var items = this.items;
2604 for(var i =0;i < items.length;i++) {
2605 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2609 this.items = nitems;
2611 // where are these used - they used to be body/close/footer
2615 //this.el.addClass([this.fieldClass, this.cls]);
2619 getAutoCreate : function(){
2624 html : this.html || ''
2629 cls : 'modal-title',
2633 if(this.specificTitle){
2639 if (this.allow_close) {
2651 if(this.size.length){
2652 size = 'modal-' + this.size;
2657 style : 'display: none',
2660 cls: "modal-dialog " + size,
2663 cls : "modal-content",
2666 cls : 'modal-header',
2671 cls : 'modal-footer',
2675 cls: 'btn-' + this.buttonPosition
2692 modal.cls += ' fade';
2698 getChildContainer : function() {
2703 getButtonContainer : function() {
2704 return this.el.select('.modal-footer div',true).first();
2707 initEvents : function()
2709 if (this.allow_close) {
2710 this.closeEl.on('click', this.hide, this);
2712 Roo.EventManager.onWindowResize(this.resize, this, true);
2719 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2720 if (this.fitwindow) {
2721 var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2722 var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2727 setSize : function(w,h)
2737 if (!this.rendered) {
2741 this.el.setStyle('display', 'block');
2743 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
2746 this.el.addClass('in');
2749 this.el.addClass('in');
2753 // not sure how we can show data in here..
2755 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2758 Roo.get(document.body).addClass("x-body-masked");
2759 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2761 this.el.setStyle('zIndex', '10001');
2763 this.fireEvent('show', this);
2768 this.items.forEach( function(e) {
2769 e.layout ? e.layout() : false;
2777 if(this.fireEvent("beforehide", this) !== false){
2779 Roo.get(document.body).removeClass("x-body-masked");
2780 this.el.removeClass('in');
2781 this.el.select('.modal-dialog', true).first().setStyle('transform','');
2783 if(this.animate){ // why
2785 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2787 this.el.setStyle('display', 'none');
2789 this.fireEvent('hide', this);
2793 addButton : function(str, cb)
2797 var b = Roo.apply({}, { html : str } );
2798 b.xns = b.xns || Roo.bootstrap;
2799 b.xtype = b.xtype || 'Button';
2800 if (typeof(b.listeners) == 'undefined') {
2801 b.listeners = { click : cb.createDelegate(this) };
2804 var btn = Roo.factory(b);
2806 btn.render(this.el.select('.modal-footer div').first());
2812 setDefaultButton : function(btn)
2814 //this.el.select('.modal-footer').()
2818 resizeTo: function(w,h)
2822 this.dialogEl.setWidth(w);
2823 if (this.diff === false) {
2824 this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2827 this.bodyEl.setHeight(h-this.diff);
2831 setContentSize : function(w, h)
2835 onButtonClick: function(btn,e)
2838 this.fireEvent('btnclick', btn.name, e);
2841 * Set the title of the Dialog
2842 * @param {String} str new Title
2844 setTitle: function(str) {
2845 this.titleEl.dom.innerHTML = str;
2848 * Set the body of the Dialog
2849 * @param {String} str new Title
2851 setBody: function(str) {
2852 this.bodyEl.dom.innerHTML = str;
2855 * Set the body of the Dialog using the template
2856 * @param {Obj} data - apply this data to the template and replace the body contents.
2858 applyBody: function(obj)
2861 Roo.log("Error - using apply Body without a template");
2864 this.tmpl.overwrite(this.bodyEl, obj);
2870 Roo.apply(Roo.bootstrap.Modal, {
2872 * Button config that displays a single OK button
2881 * Button config that displays Yes and No buttons
2897 * Button config that displays OK and Cancel buttons
2912 * Button config that displays Yes, No and Cancel buttons
2934 * messagebox - can be used as a replace
2938 * @class Roo.MessageBox
2939 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
2943 Roo.Msg.alert('Status', 'Changes saved successfully.');
2945 // Prompt for user data:
2946 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2948 // process text value...
2952 // Show a dialog using config options:
2954 title:'Save Changes?',
2955 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2956 buttons: Roo.Msg.YESNOCANCEL,
2963 Roo.bootstrap.MessageBox = function(){
2964 var dlg, opt, mask, waitTimer;
2965 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2966 var buttons, activeTextEl, bwidth;
2970 var handleButton = function(button){
2972 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2976 var handleHide = function(){
2978 dlg.el.removeClass(opt.cls);
2981 // Roo.TaskMgr.stop(waitTimer);
2982 // waitTimer = null;
2987 var updateButtons = function(b){
2990 buttons["ok"].hide();
2991 buttons["cancel"].hide();
2992 buttons["yes"].hide();
2993 buttons["no"].hide();
2994 //dlg.footer.dom.style.display = 'none';
2997 dlg.footerEl.dom.style.display = '';
2998 for(var k in buttons){
2999 if(typeof buttons[k] != "function"){
3002 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3003 width += buttons[k].el.getWidth()+15;
3013 var handleEsc = function(d, k, e){
3014 if(opt && opt.closable !== false){
3024 * Returns a reference to the underlying {@link Roo.BasicDialog} element
3025 * @return {Roo.BasicDialog} The BasicDialog element
3027 getDialog : function(){
3029 dlg = new Roo.bootstrap.Modal( {
3032 //constraintoviewport:false,
3034 //collapsible : false,
3039 //buttonAlign:"center",
3040 closeClick : function(){
3041 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3044 handleButton("cancel");
3049 dlg.on("hide", handleHide);
3051 //dlg.addKeyListener(27, handleEsc);
3053 this.buttons = buttons;
3054 var bt = this.buttonText;
3055 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3056 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3057 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3058 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3060 bodyEl = dlg.bodyEl.createChild({
3062 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3063 '<textarea class="roo-mb-textarea"></textarea>' +
3064 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
3066 msgEl = bodyEl.dom.firstChild;
3067 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3068 textboxEl.enableDisplayMode();
3069 textboxEl.addKeyListener([10,13], function(){
3070 if(dlg.isVisible() && opt && opt.buttons){
3073 }else if(opt.buttons.yes){
3074 handleButton("yes");
3078 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3079 textareaEl.enableDisplayMode();
3080 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3081 progressEl.enableDisplayMode();
3082 var pf = progressEl.dom.firstChild;
3084 pp = Roo.get(pf.firstChild);
3085 pp.setHeight(pf.offsetHeight);
3093 * Updates the message box body text
3094 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3095 * the XHTML-compliant non-breaking space character '&#160;')
3096 * @return {Roo.MessageBox} This message box
3098 updateText : function(text){
3099 if(!dlg.isVisible() && !opt.width){
3100 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
3102 msgEl.innerHTML = text || ' ';
3104 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3105 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3107 Math.min(opt.width || cw , this.maxWidth),
3108 Math.max(opt.minWidth || this.minWidth, bwidth)
3111 activeTextEl.setWidth(w);
3113 if(dlg.isVisible()){
3114 dlg.fixedcenter = false;
3116 // to big, make it scroll. = But as usual stupid IE does not support
3119 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3120 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3121 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3123 bodyEl.dom.style.height = '';
3124 bodyEl.dom.style.overflowY = '';
3127 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3129 bodyEl.dom.style.overflowX = '';
3132 dlg.setContentSize(w, bodyEl.getHeight());
3133 if(dlg.isVisible()){
3134 dlg.fixedcenter = true;
3140 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
3141 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3142 * @param {Number} value Any number between 0 and 1 (e.g., .5)
3143 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3144 * @return {Roo.MessageBox} This message box
3146 updateProgress : function(value, text){
3148 this.updateText(text);
3150 if (pp) { // weird bug on my firefox - for some reason this is not defined
3151 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3157 * Returns true if the message box is currently displayed
3158 * @return {Boolean} True if the message box is visible, else false
3160 isVisible : function(){
3161 return dlg && dlg.isVisible();
3165 * Hides the message box if it is displayed
3168 if(this.isVisible()){
3174 * Displays a new message box, or reinitializes an existing message box, based on the config options
3175 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3176 * The following config object properties are supported:
3178 Property Type Description
3179 ---------- --------------- ------------------------------------------------------------------------------------
3180 animEl String/Element An id or Element from which the message box should animate as it opens and
3181 closes (defaults to undefined)
3182 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3183 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3184 closable Boolean False to hide the top-right close button (defaults to true). Note that
3185 progress and wait dialogs will ignore this property and always hide the
3186 close button as they can only be closed programmatically.
3187 cls String A custom CSS class to apply to the message box element
3188 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3189 displayed (defaults to 75)
3190 fn Function A callback function to execute after closing the dialog. The arguments to the
3191 function will be btn (the name of the button that was clicked, if applicable,
3192 e.g. "ok"), and text (the value of the active text field, if applicable).
3193 Progress and wait dialogs will ignore this option since they do not respond to
3194 user actions and can only be closed programmatically, so any required function
3195 should be called by the same code after it closes the dialog.
3196 icon String A CSS class that provides a background image to be used as an icon for
3197 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3198 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3199 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3200 modal Boolean False to allow user interaction with the page while the message box is
3201 displayed (defaults to true)
3202 msg String A string that will replace the existing message box body text (defaults
3203 to the XHTML-compliant non-breaking space character ' ')
3204 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3205 progress Boolean True to display a progress bar (defaults to false)
3206 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3207 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3208 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3209 title String The title text
3210 value String The string value to set into the active textbox element if displayed
3211 wait Boolean True to display a progress bar (defaults to false)
3212 width Number The width of the dialog in pixels
3219 msg: 'Please enter your address:',
3221 buttons: Roo.MessageBox.OKCANCEL,
3224 animEl: 'addAddressBtn'
3227 * @param {Object} config Configuration options
3228 * @return {Roo.MessageBox} This message box
3230 show : function(options)
3233 // this causes nightmares if you show one dialog after another
3234 // especially on callbacks..
3236 if(this.isVisible()){
3239 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3240 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3241 Roo.log("New Dialog Message:" + options.msg )
3242 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3243 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3246 var d = this.getDialog();
3248 d.setTitle(opt.title || " ");
3249 d.closeEl.setDisplayed(opt.closable !== false);
3250 activeTextEl = textboxEl;
3251 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3256 textareaEl.setHeight(typeof opt.multiline == "number" ?
3257 opt.multiline : this.defaultTextHeight);
3258 activeTextEl = textareaEl;
3267 progressEl.setDisplayed(opt.progress === true);
3268 this.updateProgress(0);
3269 activeTextEl.dom.value = opt.value || "";
3271 dlg.setDefaultButton(activeTextEl);
3273 var bs = opt.buttons;
3277 }else if(bs && bs.yes){
3278 db = buttons["yes"];
3280 dlg.setDefaultButton(db);
3282 bwidth = updateButtons(opt.buttons);
3283 this.updateText(opt.msg);
3285 d.el.addClass(opt.cls);
3287 d.proxyDrag = opt.proxyDrag === true;
3288 d.modal = opt.modal !== false;
3289 d.mask = opt.modal !== false ? mask : false;
3291 // force it to the end of the z-index stack so it gets a cursor in FF
3292 document.body.appendChild(dlg.el.dom);
3293 d.animateTarget = null;
3294 d.show(options.animEl);
3300 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3301 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3302 * and closing the message box when the process is complete.
3303 * @param {String} title The title bar text
3304 * @param {String} msg The message box body text
3305 * @return {Roo.MessageBox} This message box
3307 progress : function(title, msg){
3314 minWidth: this.minProgressWidth,
3321 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3322 * If a callback function is passed it will be called after the user clicks the button, and the
3323 * id of the button that was clicked will be passed as the only parameter to the callback
3324 * (could also be the top-right close button).
3325 * @param {String} title The title bar text
3326 * @param {String} msg The message box body text
3327 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3328 * @param {Object} scope (optional) The scope of the callback function
3329 * @return {Roo.MessageBox} This message box
3331 alert : function(title, msg, fn, scope){
3344 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3345 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3346 * You are responsible for closing the message box when the process is complete.
3347 * @param {String} msg The message box body text
3348 * @param {String} title (optional) The title bar text
3349 * @return {Roo.MessageBox} This message box
3351 wait : function(msg, title){
3362 waitTimer = Roo.TaskMgr.start({
3364 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3372 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3373 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3374 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3375 * @param {String} title The title bar text
3376 * @param {String} msg The message box body text
3377 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3378 * @param {Object} scope (optional) The scope of the callback function
3379 * @return {Roo.MessageBox} This message box
3381 confirm : function(title, msg, fn, scope){
3385 buttons: this.YESNO,
3394 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3395 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3396 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3397 * (could also be the top-right close button) and the text that was entered will be passed as the two
3398 * parameters to the callback.
3399 * @param {String} title The title bar text
3400 * @param {String} msg The message box body text
3401 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3402 * @param {Object} scope (optional) The scope of the callback function
3403 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3404 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3405 * @return {Roo.MessageBox} This message box
3407 prompt : function(title, msg, fn, scope, multiline){
3411 buttons: this.OKCANCEL,
3416 multiline: multiline,
3423 * Button config that displays a single OK button
3428 * Button config that displays Yes and No buttons
3431 YESNO : {yes:true, no:true},
3433 * Button config that displays OK and Cancel buttons
3436 OKCANCEL : {ok:true, cancel:true},
3438 * Button config that displays Yes, No and Cancel buttons
3441 YESNOCANCEL : {yes:true, no:true, cancel:true},
3444 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3447 defaultTextHeight : 75,
3449 * The maximum width in pixels of the message box (defaults to 600)
3454 * The minimum width in pixels of the message box (defaults to 100)
3459 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3460 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3463 minProgressWidth : 250,
3465 * An object containing the default button text strings that can be overriden for localized language support.
3466 * Supported properties are: ok, cancel, yes and no.
3467 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3480 * Shorthand for {@link Roo.MessageBox}
3482 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3483 Roo.Msg = Roo.Msg || Roo.MessageBox;
3492 * @class Roo.bootstrap.Navbar
3493 * @extends Roo.bootstrap.Component
3494 * Bootstrap Navbar class
3497 * Create a new Navbar
3498 * @param {Object} config The config object
3502 Roo.bootstrap.Navbar = function(config){
3503 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3507 * @event beforetoggle
3508 * Fire before toggle the menu
3509 * @param {Roo.EventObject} e
3511 "beforetoggle" : true
3515 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3524 getAutoCreate : function(){
3527 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3531 initEvents :function ()
3533 //Roo.log(this.el.select('.navbar-toggle',true));
3534 this.el.select('.navbar-toggle',true).on('click', function() {
3535 if(this.fireEvent('beforetoggle', this) !== false){
3536 this.el.select('.navbar-collapse',true).toggleClass('in');
3546 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3548 var size = this.el.getSize();
3549 this.maskEl.setSize(size.width, size.height);
3550 this.maskEl.enableDisplayMode("block");
3559 getChildContainer : function()
3561 if (this.el.select('.collapse').getCount()) {
3562 return this.el.select('.collapse',true).first();
3595 * @class Roo.bootstrap.NavSimplebar
3596 * @extends Roo.bootstrap.Navbar
3597 * Bootstrap Sidebar class
3599 * @cfg {Boolean} inverse is inverted color
3601 * @cfg {String} type (nav | pills | tabs)
3602 * @cfg {Boolean} arrangement stacked | justified
3603 * @cfg {String} align (left | right) alignment
3605 * @cfg {Boolean} main (true|false) main nav bar? default false
3606 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3608 * @cfg {String} tag (header|footer|nav|div) default is nav
3614 * Create a new Sidebar
3615 * @param {Object} config The config object
3619 Roo.bootstrap.NavSimplebar = function(config){
3620 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3623 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3639 getAutoCreate : function(){
3643 tag : this.tag || 'div',
3656 this.type = this.type || 'nav';
3657 if (['tabs','pills'].indexOf(this.type)!==-1) {
3658 cfg.cn[0].cls += ' nav-' + this.type
3662 if (this.type!=='nav') {
3663 Roo.log('nav type must be nav/tabs/pills')
3665 cfg.cn[0].cls += ' navbar-nav'
3671 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3672 cfg.cn[0].cls += ' nav-' + this.arrangement;
3676 if (this.align === 'right') {
3677 cfg.cn[0].cls += ' navbar-right';
3681 cfg.cls += ' navbar-inverse';
3708 * @class Roo.bootstrap.NavHeaderbar
3709 * @extends Roo.bootstrap.NavSimplebar
3710 * Bootstrap Sidebar class
3712 * @cfg {String} brand what is brand
3713 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3714 * @cfg {String} brand_href href of the brand
3715 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
3716 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3717 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3718 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3721 * Create a new Sidebar
3722 * @param {Object} config The config object
3726 Roo.bootstrap.NavHeaderbar = function(config){
3727 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3731 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3738 desktopCenter : false,
3741 getAutoCreate : function(){
3744 tag: this.nav || 'nav',
3751 if (this.desktopCenter) {
3752 cn.push({cls : 'container', cn : []});
3759 cls: 'navbar-header',
3764 cls: 'navbar-toggle',
3765 'data-toggle': 'collapse',
3770 html: 'Toggle navigation'
3792 cls: 'collapse navbar-collapse',
3796 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3798 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3799 cfg.cls += ' navbar-' + this.position;
3801 // tag can override this..
3803 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
3806 if (this.brand !== '') {
3809 href: this.brand_href ? this.brand_href : '#',
3810 cls: 'navbar-brand',
3818 cfg.cls += ' main-nav';
3826 getHeaderChildContainer : function()
3828 if (this.srButton && this.el.select('.navbar-header').getCount()) {
3829 return this.el.select('.navbar-header',true).first();
3832 return this.getChildContainer();
3836 initEvents : function()
3838 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3840 if (this.autohide) {
3845 Roo.get(document).on('scroll',function(e) {
3846 var ns = Roo.get(document).getScroll().top;
3847 var os = prevScroll;
3851 ft.removeClass('slideDown');
3852 ft.addClass('slideUp');
3855 ft.removeClass('slideUp');
3856 ft.addClass('slideDown');
3877 * @class Roo.bootstrap.NavSidebar
3878 * @extends Roo.bootstrap.Navbar
3879 * Bootstrap Sidebar class
3882 * Create a new Sidebar
3883 * @param {Object} config The config object
3887 Roo.bootstrap.NavSidebar = function(config){
3888 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3891 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
3893 sidebar : true, // used by Navbar Item and NavbarGroup at present...
3895 getAutoCreate : function(){
3900 cls: 'sidebar sidebar-nav'
3922 * @class Roo.bootstrap.NavGroup
3923 * @extends Roo.bootstrap.Component
3924 * Bootstrap NavGroup class
3925 * @cfg {String} align (left|right)
3926 * @cfg {Boolean} inverse
3927 * @cfg {String} type (nav|pills|tab) default nav
3928 * @cfg {String} navId - reference Id for navbar.
3932 * Create a new nav group
3933 * @param {Object} config The config object
3936 Roo.bootstrap.NavGroup = function(config){
3937 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3940 Roo.bootstrap.NavGroup.register(this);
3944 * Fires when the active item changes
3945 * @param {Roo.bootstrap.NavGroup} this
3946 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3947 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
3954 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
3965 getAutoCreate : function()
3967 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3974 if (['tabs','pills'].indexOf(this.type)!==-1) {
3975 cfg.cls += ' nav-' + this.type
3977 if (this.type!=='nav') {
3978 Roo.log('nav type must be nav/tabs/pills')
3980 cfg.cls += ' navbar-nav'
3983 if (this.parent().sidebar) {
3986 cls: 'dashboard-menu sidebar-menu'
3992 if (this.form === true) {
3998 if (this.align === 'right') {
3999 cfg.cls += ' navbar-right';
4001 cfg.cls += ' navbar-left';
4005 if (this.align === 'right') {
4006 cfg.cls += ' navbar-right';
4010 cfg.cls += ' navbar-inverse';
4018 * sets the active Navigation item
4019 * @param {Roo.bootstrap.NavItem} the new current navitem
4021 setActiveItem : function(item)
4024 Roo.each(this.navItems, function(v){
4029 v.setActive(false, true);
4036 item.setActive(true, true);
4037 this.fireEvent('changed', this, item, prev);
4042 * gets the active Navigation item
4043 * @return {Roo.bootstrap.NavItem} the current navitem
4045 getActive : function()
4049 Roo.each(this.navItems, function(v){
4060 indexOfNav : function()
4064 Roo.each(this.navItems, function(v,i){
4075 * adds a Navigation item
4076 * @param {Roo.bootstrap.NavItem} the navitem to add
4078 addItem : function(cfg)
4080 var cn = new Roo.bootstrap.NavItem(cfg);
4082 cn.parentId = this.id;
4083 cn.onRender(this.el, null);
4087 * register a Navigation item
4088 * @param {Roo.bootstrap.NavItem} the navitem to add
4090 register : function(item)
4092 this.navItems.push( item);
4093 item.navId = this.navId;
4098 * clear all the Navigation item
4101 clearAll : function()
4104 this.el.dom.innerHTML = '';
4107 getNavItem: function(tabId)
4110 Roo.each(this.navItems, function(e) {
4111 if (e.tabId == tabId) {
4121 setActiveNext : function()
4123 var i = this.indexOfNav(this.getActive());
4124 if (i > this.navItems.length) {
4127 this.setActiveItem(this.navItems[i+1]);
4129 setActivePrev : function()
4131 var i = this.indexOfNav(this.getActive());
4135 this.setActiveItem(this.navItems[i-1]);
4137 clearWasActive : function(except) {
4138 Roo.each(this.navItems, function(e) {
4139 if (e.tabId != except.tabId && e.was_active) {
4140 e.was_active = false;
4147 getWasActive : function ()
4150 Roo.each(this.navItems, function(e) {
4165 Roo.apply(Roo.bootstrap.NavGroup, {
4169 * register a Navigation Group
4170 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4172 register : function(navgrp)
4174 this.groups[navgrp.navId] = navgrp;
4178 * fetch a Navigation Group based on the navigation ID
4179 * @param {string} the navgroup to add
4180 * @returns {Roo.bootstrap.NavGroup} the navgroup
4182 get: function(navId) {
4183 if (typeof(this.groups[navId]) == 'undefined') {
4185 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4187 return this.groups[navId] ;
4202 * @class Roo.bootstrap.NavItem
4203 * @extends Roo.bootstrap.Component
4204 * Bootstrap Navbar.NavItem class
4205 * @cfg {String} href link to
4206 * @cfg {String} html content of button
4207 * @cfg {String} badge text inside badge
4208 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4209 * @cfg {String} glyphicon name of glyphicon
4210 * @cfg {String} icon name of font awesome icon
4211 * @cfg {Boolean} active Is item active
4212 * @cfg {Boolean} disabled Is item disabled
4214 * @cfg {Boolean} preventDefault (true | false) default false
4215 * @cfg {String} tabId the tab that this item activates.
4216 * @cfg {String} tagtype (a|span) render as a href or span?
4217 * @cfg {Boolean} animateRef (true|false) link to element default false
4220 * Create a new Navbar Item
4221 * @param {Object} config The config object
4223 Roo.bootstrap.NavItem = function(config){
4224 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4229 * The raw click event for the entire grid.
4230 * @param {Roo.EventObject} e
4235 * Fires when the active item active state changes
4236 * @param {Roo.bootstrap.NavItem} this
4237 * @param {boolean} state the new state
4243 * Fires when scroll to element
4244 * @param {Roo.bootstrap.NavItem} this
4245 * @param {Object} options
4246 * @param {Roo.EventObject} e
4254 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4262 preventDefault : false,
4269 getAutoCreate : function(){
4278 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4280 if (this.disabled) {
4281 cfg.cls += ' disabled';
4284 if (this.href || this.html || this.glyphicon || this.icon) {
4288 href : this.href || "#",
4289 html: this.html || ''
4294 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4297 if(this.glyphicon) {
4298 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4303 cfg.cn[0].html += " <span class='caret'></span>";
4307 if (this.badge !== '') {
4309 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4317 initEvents: function()
4319 if (typeof (this.menu) != 'undefined') {
4320 this.menu.parentType = this.xtype;
4321 this.menu.triggerEl = this.el;
4322 this.menu = this.addxtype(Roo.apply({}, this.menu));
4325 this.el.select('a',true).on('click', this.onClick, this);
4327 if(this.tagtype == 'span'){
4328 this.el.select('span',true).on('click', this.onClick, this);
4331 // at this point parent should be available..
4332 this.parent().register(this);
4335 onClick : function(e)
4337 if (e.getTarget('.dropdown-menu-item')) {
4338 // did you click on a menu itemm.... - then don't trigger onclick..
4343 this.preventDefault ||
4346 Roo.log("NavItem - prevent Default?");
4350 if (this.disabled) {
4354 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4355 if (tg && tg.transition) {
4356 Roo.log("waiting for the transitionend");
4362 //Roo.log("fire event clicked");
4363 if(this.fireEvent('click', this, e) === false){
4367 if(this.tagtype == 'span'){
4371 //Roo.log(this.href);
4372 var ael = this.el.select('a',true).first();
4375 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4376 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4377 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4378 return; // ignore... - it's a 'hash' to another page.
4380 Roo.log("NavItem - prevent Default?");
4382 this.scrollToElement(e);
4386 var p = this.parent();
4388 if (['tabs','pills'].indexOf(p.type)!==-1) {
4389 if (typeof(p.setActiveItem) !== 'undefined') {
4390 p.setActiveItem(this);
4394 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4395 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4396 // remove the collapsed menu expand...
4397 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4401 isActive: function () {
4404 setActive : function(state, fire, is_was_active)
4406 if (this.active && !state && this.navId) {
4407 this.was_active = true;
4408 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4410 nv.clearWasActive(this);
4414 this.active = state;
4417 this.el.removeClass('active');
4418 } else if (!this.el.hasClass('active')) {
4419 this.el.addClass('active');
4422 this.fireEvent('changed', this, state);
4425 // show a panel if it's registered and related..
4427 if (!this.navId || !this.tabId || !state || is_was_active) {
4431 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4435 var pan = tg.getPanelByName(this.tabId);
4439 // if we can not flip to new panel - go back to old nav highlight..
4440 if (false == tg.showPanel(pan)) {
4441 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4443 var onav = nv.getWasActive();
4445 onav.setActive(true, false, true);
4454 // this should not be here...
4455 setDisabled : function(state)
4457 this.disabled = state;
4459 this.el.removeClass('disabled');
4460 } else if (!this.el.hasClass('disabled')) {
4461 this.el.addClass('disabled');
4467 * Fetch the element to display the tooltip on.
4468 * @return {Roo.Element} defaults to this.el
4470 tooltipEl : function()
4472 return this.el.select('' + this.tagtype + '', true).first();
4475 scrollToElement : function(e)
4477 var c = document.body;
4480 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4482 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4483 c = document.documentElement;
4486 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4492 var o = target.calcOffsetsTo(c);
4499 this.fireEvent('scrollto', this, options, e);
4501 Roo.get(c).scrollTo('top', options.value, true);
4514 * <span> icon </span>
4515 * <span> text </span>
4516 * <span>badge </span>
4520 * @class Roo.bootstrap.NavSidebarItem
4521 * @extends Roo.bootstrap.NavItem
4522 * Bootstrap Navbar.NavSidebarItem class
4523 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4524 * {bool} open is the menu open
4526 * Create a new Navbar Button
4527 * @param {Object} config The config object
4529 Roo.bootstrap.NavSidebarItem = function(config){
4530 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4535 * The raw click event for the entire grid.
4536 * @param {Roo.EventObject} e
4541 * Fires when the active item active state changes
4542 * @param {Roo.bootstrap.NavSidebarItem} this
4543 * @param {boolean} state the new state
4551 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4553 badgeWeight : 'default',
4557 getAutoCreate : function(){
4562 href : this.href || '#',
4574 html : this.html || ''
4579 cfg.cls += ' active';
4582 if (this.disabled) {
4583 cfg.cls += ' disabled';
4586 cfg.cls += ' open x-open';
4589 if (this.glyphicon || this.icon) {
4590 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4591 a.cn.push({ tag : 'i', cls : c }) ;
4596 if (this.badge !== '') {
4598 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4602 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4603 a.cls += 'dropdown-toggle treeview' ;
4611 initEvents : function()
4613 if (typeof (this.menu) != 'undefined') {
4614 this.menu.parentType = this.xtype;
4615 this.menu.triggerEl = this.el;
4616 this.menu = this.addxtype(Roo.apply({}, this.menu));
4619 this.el.on('click', this.onClick, this);
4622 if(this.badge !== ''){
4624 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4629 onClick : function(e)
4636 if(this.preventDefault){
4640 this.fireEvent('click', this);
4643 disable : function()
4645 this.setDisabled(true);
4650 this.setDisabled(false);
4653 setDisabled : function(state)
4655 if(this.disabled == state){
4659 this.disabled = state;
4662 this.el.addClass('disabled');
4666 this.el.removeClass('disabled');
4671 setActive : function(state)
4673 if(this.active == state){
4677 this.active = state;
4680 this.el.addClass('active');
4684 this.el.removeClass('active');
4689 isActive: function ()
4694 setBadge : function(str)
4700 this.badgeEl.dom.innerHTML = str;
4717 * @class Roo.bootstrap.Row
4718 * @extends Roo.bootstrap.Component
4719 * Bootstrap Row class (contains columns...)
4723 * @param {Object} config The config object
4726 Roo.bootstrap.Row = function(config){
4727 Roo.bootstrap.Row.superclass.constructor.call(this, config);
4730 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
4732 getAutoCreate : function(){
4751 * @class Roo.bootstrap.Element
4752 * @extends Roo.bootstrap.Component
4753 * Bootstrap Element class
4754 * @cfg {String} html contents of the element
4755 * @cfg {String} tag tag of the element
4756 * @cfg {String} cls class of the element
4757 * @cfg {Boolean} preventDefault (true|false) default false
4758 * @cfg {Boolean} clickable (true|false) default false
4761 * Create a new Element
4762 * @param {Object} config The config object
4765 Roo.bootstrap.Element = function(config){
4766 Roo.bootstrap.Element.superclass.constructor.call(this, config);
4772 * When a element is chick
4773 * @param {Roo.bootstrap.Element} this
4774 * @param {Roo.EventObject} e
4780 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
4785 preventDefault: false,
4788 getAutoCreate : function(){
4799 initEvents: function()
4801 Roo.bootstrap.Element.superclass.initEvents.call(this);
4804 this.el.on('click', this.onClick, this);
4809 onClick : function(e)
4811 if(this.preventDefault){
4815 this.fireEvent('click', this, e);
4818 getValue : function()
4820 return this.el.dom.innerHTML;
4823 setValue : function(value)
4825 this.el.dom.innerHTML = value;
4840 * @class Roo.bootstrap.Pagination
4841 * @extends Roo.bootstrap.Component
4842 * Bootstrap Pagination class
4843 * @cfg {String} size xs | sm | md | lg
4844 * @cfg {Boolean} inverse false | true
4847 * Create a new Pagination
4848 * @param {Object} config The config object
4851 Roo.bootstrap.Pagination = function(config){
4852 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4855 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
4861 getAutoCreate : function(){
4867 cfg.cls += ' inverse';
4873 cfg.cls += " " + this.cls;
4891 * @class Roo.bootstrap.PaginationItem
4892 * @extends Roo.bootstrap.Component
4893 * Bootstrap PaginationItem class
4894 * @cfg {String} html text
4895 * @cfg {String} href the link
4896 * @cfg {Boolean} preventDefault (true | false) default true
4897 * @cfg {Boolean} active (true | false) default false
4898 * @cfg {Boolean} disabled default false
4902 * Create a new PaginationItem
4903 * @param {Object} config The config object
4907 Roo.bootstrap.PaginationItem = function(config){
4908 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4913 * The raw click event for the entire grid.
4914 * @param {Roo.EventObject} e
4920 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
4924 preventDefault: true,
4929 getAutoCreate : function(){
4935 href : this.href ? this.href : '#',
4936 html : this.html ? this.html : ''
4946 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4950 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4956 initEvents: function() {
4958 this.el.on('click', this.onClick, this);
4961 onClick : function(e)
4963 Roo.log('PaginationItem on click ');
4964 if(this.preventDefault){
4972 this.fireEvent('click', this, e);
4988 * @class Roo.bootstrap.Slider
4989 * @extends Roo.bootstrap.Component
4990 * Bootstrap Slider class
4993 * Create a new Slider
4994 * @param {Object} config The config object
4997 Roo.bootstrap.Slider = function(config){
4998 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5001 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
5003 getAutoCreate : function(){
5007 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5011 cls: 'ui-slider-handle ui-state-default ui-corner-all'
5023 * Ext JS Library 1.1.1
5024 * Copyright(c) 2006-2007, Ext JS, LLC.
5026 * Originally Released Under LGPL - original licence link has changed is not relivant.
5029 * <script type="text/javascript">
5034 * @class Roo.grid.ColumnModel
5035 * @extends Roo.util.Observable
5036 * This is the default implementation of a ColumnModel used by the Grid. It defines
5037 * the columns in the grid.
5040 var colModel = new Roo.grid.ColumnModel([
5041 {header: "Ticker", width: 60, sortable: true, locked: true},
5042 {header: "Company Name", width: 150, sortable: true},
5043 {header: "Market Cap.", width: 100, sortable: true},
5044 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5045 {header: "Employees", width: 100, sortable: true, resizable: false}
5050 * The config options listed for this class are options which may appear in each
5051 * individual column definition.
5052 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5054 * @param {Object} config An Array of column config objects. See this class's
5055 * config objects for details.
5057 Roo.grid.ColumnModel = function(config){
5059 * The config passed into the constructor
5061 this.config = config;
5064 // if no id, create one
5065 // if the column does not have a dataIndex mapping,
5066 // map it to the order it is in the config
5067 for(var i = 0, len = config.length; i < len; i++){
5069 if(typeof c.dataIndex == "undefined"){
5072 if(typeof c.renderer == "string"){
5073 c.renderer = Roo.util.Format[c.renderer];
5075 if(typeof c.id == "undefined"){
5078 if(c.editor && c.editor.xtype){
5079 c.editor = Roo.factory(c.editor, Roo.grid);
5081 if(c.editor && c.editor.isFormField){
5082 c.editor = new Roo.grid.GridEditor(c.editor);
5084 this.lookup[c.id] = c;
5088 * The width of columns which have no width specified (defaults to 100)
5091 this.defaultWidth = 100;
5094 * Default sortable of columns which have no sortable specified (defaults to false)
5097 this.defaultSortable = false;
5101 * @event widthchange
5102 * Fires when the width of a column changes.
5103 * @param {ColumnModel} this
5104 * @param {Number} columnIndex The column index
5105 * @param {Number} newWidth The new width
5107 "widthchange": true,
5109 * @event headerchange
5110 * Fires when the text of a header changes.
5111 * @param {ColumnModel} this
5112 * @param {Number} columnIndex The column index
5113 * @param {Number} newText The new header text
5115 "headerchange": true,
5117 * @event hiddenchange
5118 * Fires when a column is hidden or "unhidden".
5119 * @param {ColumnModel} this
5120 * @param {Number} columnIndex The column index
5121 * @param {Boolean} hidden true if hidden, false otherwise
5123 "hiddenchange": true,
5125 * @event columnmoved
5126 * Fires when a column is moved.
5127 * @param {ColumnModel} this
5128 * @param {Number} oldIndex
5129 * @param {Number} newIndex
5131 "columnmoved" : true,
5133 * @event columlockchange
5134 * Fires when a column's locked state is changed
5135 * @param {ColumnModel} this
5136 * @param {Number} colIndex
5137 * @param {Boolean} locked true if locked
5139 "columnlockchange" : true
5141 Roo.grid.ColumnModel.superclass.constructor.call(this);
5143 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5145 * @cfg {String} header The header text to display in the Grid view.
5148 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5149 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5150 * specified, the column's index is used as an index into the Record's data Array.
5153 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5154 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5157 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5158 * Defaults to the value of the {@link #defaultSortable} property.
5159 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5162 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5165 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5168 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5171 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5174 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5175 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5176 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5177 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5180 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5183 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5186 * @cfg {String} cursor (Optional)
5189 * @cfg {String} tooltip (Optional)
5192 * @cfg {Number} xs (Optional)
5195 * @cfg {Number} sm (Optional)
5198 * @cfg {Number} md (Optional)
5201 * @cfg {Number} lg (Optional)
5204 * Returns the id of the column at the specified index.
5205 * @param {Number} index The column index
5206 * @return {String} the id
5208 getColumnId : function(index){
5209 return this.config[index].id;
5213 * Returns the column for a specified id.
5214 * @param {String} id The column id
5215 * @return {Object} the column
5217 getColumnById : function(id){
5218 return this.lookup[id];
5223 * Returns the column for a specified dataIndex.
5224 * @param {String} dataIndex The column dataIndex
5225 * @return {Object|Boolean} the column or false if not found
5227 getColumnByDataIndex: function(dataIndex){
5228 var index = this.findColumnIndex(dataIndex);
5229 return index > -1 ? this.config[index] : false;
5233 * Returns the index for a specified column id.
5234 * @param {String} id The column id
5235 * @return {Number} the index, or -1 if not found
5237 getIndexById : function(id){
5238 for(var i = 0, len = this.config.length; i < len; i++){
5239 if(this.config[i].id == id){
5247 * Returns the index for a specified column dataIndex.
5248 * @param {String} dataIndex The column dataIndex
5249 * @return {Number} the index, or -1 if not found
5252 findColumnIndex : function(dataIndex){
5253 for(var i = 0, len = this.config.length; i < len; i++){
5254 if(this.config[i].dataIndex == dataIndex){
5262 moveColumn : function(oldIndex, newIndex){
5263 var c = this.config[oldIndex];
5264 this.config.splice(oldIndex, 1);
5265 this.config.splice(newIndex, 0, c);
5266 this.dataMap = null;
5267 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5270 isLocked : function(colIndex){
5271 return this.config[colIndex].locked === true;
5274 setLocked : function(colIndex, value, suppressEvent){
5275 if(this.isLocked(colIndex) == value){
5278 this.config[colIndex].locked = value;
5280 this.fireEvent("columnlockchange", this, colIndex, value);
5284 getTotalLockedWidth : function(){
5286 for(var i = 0; i < this.config.length; i++){
5287 if(this.isLocked(i) && !this.isHidden(i)){
5288 this.totalWidth += this.getColumnWidth(i);
5294 getLockedCount : function(){
5295 for(var i = 0, len = this.config.length; i < len; i++){
5296 if(!this.isLocked(i)){
5301 return this.config.length;
5305 * Returns the number of columns.
5308 getColumnCount : function(visibleOnly){
5309 if(visibleOnly === true){
5311 for(var i = 0, len = this.config.length; i < len; i++){
5312 if(!this.isHidden(i)){
5318 return this.config.length;
5322 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5323 * @param {Function} fn
5324 * @param {Object} scope (optional)
5325 * @return {Array} result
5327 getColumnsBy : function(fn, scope){
5329 for(var i = 0, len = this.config.length; i < len; i++){
5330 var c = this.config[i];
5331 if(fn.call(scope||this, c, i) === true){
5339 * Returns true if the specified column is sortable.
5340 * @param {Number} col The column index
5343 isSortable : function(col){
5344 if(typeof this.config[col].sortable == "undefined"){
5345 return this.defaultSortable;
5347 return this.config[col].sortable;
5351 * Returns the rendering (formatting) function defined for the column.
5352 * @param {Number} col The column index.
5353 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5355 getRenderer : function(col){
5356 if(!this.config[col].renderer){
5357 return Roo.grid.ColumnModel.defaultRenderer;
5359 return this.config[col].renderer;
5363 * Sets the rendering (formatting) function for a column.
5364 * @param {Number} col The column index
5365 * @param {Function} fn The function to use to process the cell's raw data
5366 * to return HTML markup for the grid view. The render function is called with
5367 * the following parameters:<ul>
5368 * <li>Data value.</li>
5369 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5370 * <li>css A CSS style string to apply to the table cell.</li>
5371 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5372 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5373 * <li>Row index</li>
5374 * <li>Column index</li>
5375 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5377 setRenderer : function(col, fn){
5378 this.config[col].renderer = fn;
5382 * Returns the width for the specified column.
5383 * @param {Number} col The column index
5386 getColumnWidth : function(col){
5387 return this.config[col].width * 1 || this.defaultWidth;
5391 * Sets the width for a column.
5392 * @param {Number} col The column index
5393 * @param {Number} width The new width
5395 setColumnWidth : function(col, width, suppressEvent){
5396 this.config[col].width = width;
5397 this.totalWidth = null;
5399 this.fireEvent("widthchange", this, col, width);
5404 * Returns the total width of all columns.
5405 * @param {Boolean} includeHidden True to include hidden column widths
5408 getTotalWidth : function(includeHidden){
5409 if(!this.totalWidth){
5410 this.totalWidth = 0;
5411 for(var i = 0, len = this.config.length; i < len; i++){
5412 if(includeHidden || !this.isHidden(i)){
5413 this.totalWidth += this.getColumnWidth(i);
5417 return this.totalWidth;
5421 * Returns the header for the specified column.
5422 * @param {Number} col The column index
5425 getColumnHeader : function(col){
5426 return this.config[col].header;
5430 * Sets the header for a column.
5431 * @param {Number} col The column index
5432 * @param {String} header The new header
5434 setColumnHeader : function(col, header){
5435 this.config[col].header = header;
5436 this.fireEvent("headerchange", this, col, header);
5440 * Returns the tooltip for the specified column.
5441 * @param {Number} col The column index
5444 getColumnTooltip : function(col){
5445 return this.config[col].tooltip;
5448 * Sets the tooltip for a column.
5449 * @param {Number} col The column index
5450 * @param {String} tooltip The new tooltip
5452 setColumnTooltip : function(col, tooltip){
5453 this.config[col].tooltip = tooltip;
5457 * Returns the dataIndex for the specified column.
5458 * @param {Number} col The column index
5461 getDataIndex : function(col){
5462 return this.config[col].dataIndex;
5466 * Sets the dataIndex for a column.
5467 * @param {Number} col The column index
5468 * @param {Number} dataIndex The new dataIndex
5470 setDataIndex : function(col, dataIndex){
5471 this.config[col].dataIndex = dataIndex;
5477 * Returns true if the cell is editable.
5478 * @param {Number} colIndex The column index
5479 * @param {Number} rowIndex The row index - this is nto actually used..?
5482 isCellEditable : function(colIndex, rowIndex){
5483 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5487 * Returns the editor defined for the cell/column.
5488 * return false or null to disable editing.
5489 * @param {Number} colIndex The column index
5490 * @param {Number} rowIndex The row index
5493 getCellEditor : function(colIndex, rowIndex){
5494 return this.config[colIndex].editor;
5498 * Sets if a column is editable.
5499 * @param {Number} col The column index
5500 * @param {Boolean} editable True if the column is editable
5502 setEditable : function(col, editable){
5503 this.config[col].editable = editable;
5508 * Returns true if the column is hidden.
5509 * @param {Number} colIndex The column index
5512 isHidden : function(colIndex){
5513 return this.config[colIndex].hidden;
5518 * Returns true if the column width cannot be changed
5520 isFixed : function(colIndex){
5521 return this.config[colIndex].fixed;
5525 * Returns true if the column can be resized
5528 isResizable : function(colIndex){
5529 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5532 * Sets if a column is hidden.
5533 * @param {Number} colIndex The column index
5534 * @param {Boolean} hidden True if the column is hidden
5536 setHidden : function(colIndex, hidden){
5537 this.config[colIndex].hidden = hidden;
5538 this.totalWidth = null;
5539 this.fireEvent("hiddenchange", this, colIndex, hidden);
5543 * Sets the editor for a column.
5544 * @param {Number} col The column index
5545 * @param {Object} editor The editor object
5547 setEditor : function(col, editor){
5548 this.config[col].editor = editor;
5552 Roo.grid.ColumnModel.defaultRenderer = function(value)
5554 if(typeof value == "object") {
5557 if(typeof value == "string" && value.length < 1){
5561 return String.format("{0}", value);
5564 // Alias for backwards compatibility
5565 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5568 * Ext JS Library 1.1.1
5569 * Copyright(c) 2006-2007, Ext JS, LLC.
5571 * Originally Released Under LGPL - original licence link has changed is not relivant.
5574 * <script type="text/javascript">
5578 * @class Roo.LoadMask
5579 * A simple utility class for generically masking elements while loading data. If the element being masked has
5580 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5581 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5582 * element's UpdateManager load indicator and will be destroyed after the initial load.
5584 * Create a new LoadMask
5585 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5586 * @param {Object} config The config object
5588 Roo.LoadMask = function(el, config){
5589 this.el = Roo.get(el);
5590 Roo.apply(this, config);
5592 this.store.on('beforeload', this.onBeforeLoad, this);
5593 this.store.on('load', this.onLoad, this);
5594 this.store.on('loadexception', this.onLoadException, this);
5595 this.removeMask = false;
5597 var um = this.el.getUpdateManager();
5598 um.showLoadIndicator = false; // disable the default indicator
5599 um.on('beforeupdate', this.onBeforeLoad, this);
5600 um.on('update', this.onLoad, this);
5601 um.on('failure', this.onLoad, this);
5602 this.removeMask = true;
5606 Roo.LoadMask.prototype = {
5608 * @cfg {Boolean} removeMask
5609 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5610 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5614 * The text to display in a centered loading message box (defaults to 'Loading...')
5618 * @cfg {String} msgCls
5619 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5621 msgCls : 'x-mask-loading',
5624 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5630 * Disables the mask to prevent it from being displayed
5632 disable : function(){
5633 this.disabled = true;
5637 * Enables the mask so that it can be displayed
5639 enable : function(){
5640 this.disabled = false;
5643 onLoadException : function()
5647 if (typeof(arguments[3]) != 'undefined') {
5648 Roo.MessageBox.alert("Error loading",arguments[3]);
5652 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5653 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5662 this.el.unmask(this.removeMask);
5667 this.el.unmask(this.removeMask);
5671 onBeforeLoad : function(){
5673 this.el.mask(this.msg, this.msgCls);
5678 destroy : function(){
5680 this.store.un('beforeload', this.onBeforeLoad, this);
5681 this.store.un('load', this.onLoad, this);
5682 this.store.un('loadexception', this.onLoadException, this);
5684 var um = this.el.getUpdateManager();
5685 um.un('beforeupdate', this.onBeforeLoad, this);
5686 um.un('update', this.onLoad, this);
5687 um.un('failure', this.onLoad, this);
5698 * @class Roo.bootstrap.Table
5699 * @extends Roo.bootstrap.Component
5700 * Bootstrap Table class
5701 * @cfg {String} cls table class
5702 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5703 * @cfg {String} bgcolor Specifies the background color for a table
5704 * @cfg {Number} border Specifies whether the table cells should have borders or not
5705 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5706 * @cfg {Number} cellspacing Specifies the space between cells
5707 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5708 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5709 * @cfg {String} sortable Specifies that the table should be sortable
5710 * @cfg {String} summary Specifies a summary of the content of a table
5711 * @cfg {Number} width Specifies the width of a table
5712 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5714 * @cfg {boolean} striped Should the rows be alternative striped
5715 * @cfg {boolean} bordered Add borders to the table
5716 * @cfg {boolean} hover Add hover highlighting
5717 * @cfg {boolean} condensed Format condensed
5718 * @cfg {boolean} responsive Format condensed
5719 * @cfg {Boolean} loadMask (true|false) default false
5720 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5721 * @cfg {Boolean} headerShow (true|false) generate thead, default true
5722 * @cfg {Boolean} rowSelection (true|false) default false
5723 * @cfg {Boolean} cellSelection (true|false) default false
5724 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5725 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
5729 * Create a new Table
5730 * @param {Object} config The config object
5733 Roo.bootstrap.Table = function(config){
5734 Roo.bootstrap.Table.superclass.constructor.call(this, config);
5739 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5740 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5741 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5742 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5744 this.sm = this.sm || {xtype: 'RowSelectionModel'};
5746 this.sm.grid = this;
5747 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5748 this.sm = this.selModel;
5749 this.sm.xmodule = this.xmodule || false;
5752 if (this.cm && typeof(this.cm.config) == 'undefined') {
5753 this.colModel = new Roo.grid.ColumnModel(this.cm);
5754 this.cm = this.colModel;
5755 this.cm.xmodule = this.xmodule || false;
5758 this.store= Roo.factory(this.store, Roo.data);
5759 this.ds = this.store;
5760 this.ds.xmodule = this.xmodule || false;
5763 if (this.footer && this.store) {
5764 this.footer.dataSource = this.ds;
5765 this.footer = Roo.factory(this.footer);
5772 * Fires when a cell is clicked
5773 * @param {Roo.bootstrap.Table} this
5774 * @param {Roo.Element} el
5775 * @param {Number} rowIndex
5776 * @param {Number} columnIndex
5777 * @param {Roo.EventObject} e
5781 * @event celldblclick
5782 * Fires when a cell is double clicked
5783 * @param {Roo.bootstrap.Table} this
5784 * @param {Roo.Element} el
5785 * @param {Number} rowIndex
5786 * @param {Number} columnIndex
5787 * @param {Roo.EventObject} e
5789 "celldblclick" : true,
5792 * Fires when a row is clicked
5793 * @param {Roo.bootstrap.Table} this
5794 * @param {Roo.Element} el
5795 * @param {Number} rowIndex
5796 * @param {Roo.EventObject} e
5800 * @event rowdblclick
5801 * Fires when a row is double clicked
5802 * @param {Roo.bootstrap.Table} this
5803 * @param {Roo.Element} el
5804 * @param {Number} rowIndex
5805 * @param {Roo.EventObject} e
5807 "rowdblclick" : true,
5810 * Fires when a mouseover occur
5811 * @param {Roo.bootstrap.Table} this
5812 * @param {Roo.Element} el
5813 * @param {Number} rowIndex
5814 * @param {Number} columnIndex
5815 * @param {Roo.EventObject} e
5820 * Fires when a mouseout occur
5821 * @param {Roo.bootstrap.Table} this
5822 * @param {Roo.Element} el
5823 * @param {Number} rowIndex
5824 * @param {Number} columnIndex
5825 * @param {Roo.EventObject} e
5830 * Fires when a row is rendered, so you can change add a style to it.
5831 * @param {Roo.bootstrap.Table} this
5832 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
5836 * @event rowsrendered
5837 * Fires when all the rows have been rendered
5838 * @param {Roo.bootstrap.Table} this
5840 'rowsrendered' : true,
5842 * @event contextmenu
5843 * The raw contextmenu event for the entire grid.
5844 * @param {Roo.EventObject} e
5846 "contextmenu" : true,
5848 * @event rowcontextmenu
5849 * Fires when a row is right clicked
5850 * @param {Roo.bootstrap.Table} this
5851 * @param {Number} rowIndex
5852 * @param {Roo.EventObject} e
5854 "rowcontextmenu" : true,
5856 * @event cellcontextmenu
5857 * Fires when a cell is right clicked
5858 * @param {Roo.bootstrap.Table} this
5859 * @param {Number} rowIndex
5860 * @param {Number} cellIndex
5861 * @param {Roo.EventObject} e
5863 "cellcontextmenu" : true,
5865 * @event headercontextmenu
5866 * Fires when a header is right clicked
5867 * @param {Roo.bootstrap.Table} this
5868 * @param {Number} columnIndex
5869 * @param {Roo.EventObject} e
5871 "headercontextmenu" : true
5875 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
5901 rowSelection : false,
5902 cellSelection : false,
5905 // Roo.Element - the tbody
5907 // Roo.Element - thead element
5910 container: false, // used by gridpanel...
5912 getAutoCreate : function()
5914 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5921 if (this.scrollBody) {
5922 cfg.cls += ' table-body-fixed';
5925 cfg.cls += ' table-striped';
5929 cfg.cls += ' table-hover';
5931 if (this.bordered) {
5932 cfg.cls += ' table-bordered';
5934 if (this.condensed) {
5935 cfg.cls += ' table-condensed';
5937 if (this.responsive) {
5938 cfg.cls += ' table-responsive';
5942 cfg.cls+= ' ' +this.cls;
5945 // this lot should be simplifed...
5948 cfg.align=this.align;
5951 cfg.bgcolor=this.bgcolor;
5954 cfg.border=this.border;
5956 if (this.cellpadding) {
5957 cfg.cellpadding=this.cellpadding;
5959 if (this.cellspacing) {
5960 cfg.cellspacing=this.cellspacing;
5963 cfg.frame=this.frame;
5966 cfg.rules=this.rules;
5968 if (this.sortable) {
5969 cfg.sortable=this.sortable;
5972 cfg.summary=this.summary;
5975 cfg.width=this.width;
5978 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5981 if(this.store || this.cm){
5982 if(this.headerShow){
5983 cfg.cn.push(this.renderHeader());
5986 cfg.cn.push(this.renderBody());
5988 if(this.footerShow){
5989 cfg.cn.push(this.renderFooter());
5991 // where does this come from?
5992 //cfg.cls+= ' TableGrid';
5995 return { cn : [ cfg ] };
5998 initEvents : function()
6000 if(!this.store || !this.cm){
6003 if (this.selModel) {
6004 this.selModel.initEvents();
6008 //Roo.log('initEvents with ds!!!!');
6010 this.mainBody = this.el.select('tbody', true).first();
6011 this.mainHead = this.el.select('thead', true).first();
6018 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6019 e.on('click', _this.sort, _this);
6022 this.mainBody.on("click", this.onClick, this);
6023 this.mainBody.on("dblclick", this.onDblClick, this);
6025 // why is this done????? = it breaks dialogs??
6026 //this.parent().el.setStyle('position', 'relative');
6030 this.footer.parentId = this.id;
6031 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6034 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6036 this.store.on('load', this.onLoad, this);
6037 this.store.on('beforeload', this.onBeforeLoad, this);
6038 this.store.on('update', this.onUpdate, this);
6039 this.store.on('add', this.onAdd, this);
6040 this.store.on("clear", this.clear, this);
6042 this.el.on("contextmenu", this.onContextMenu, this);
6044 this.mainBody.on('scroll', this.onBodyScroll, this);
6049 onContextMenu : function(e, t)
6051 this.processEvent("contextmenu", e);
6054 processEvent : function(name, e)
6056 if (name != 'touchstart' ) {
6057 this.fireEvent(name, e);
6060 var t = e.getTarget();
6062 var cell = Roo.get(t);
6068 if(cell.findParent('tfoot', false, true)){
6072 if(cell.findParent('thead', false, true)){
6074 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6075 cell = Roo.get(t).findParent('th', false, true);
6077 Roo.log("failed to find th in thead?");
6078 Roo.log(e.getTarget());
6083 var cellIndex = cell.dom.cellIndex;
6085 var ename = name == 'touchstart' ? 'click' : name;
6086 this.fireEvent("header" + ename, this, cellIndex, e);
6091 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6092 cell = Roo.get(t).findParent('td', false, true);
6094 Roo.log("failed to find th in tbody?");
6095 Roo.log(e.getTarget());
6100 var row = cell.findParent('tr', false, true);
6101 var cellIndex = cell.dom.cellIndex;
6102 var rowIndex = row.dom.rowIndex - 1;
6106 this.fireEvent("row" + name, this, rowIndex, e);
6110 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6116 onMouseover : function(e, el)
6118 var cell = Roo.get(el);
6124 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6125 cell = cell.findParent('td', false, true);
6128 var row = cell.findParent('tr', false, true);
6129 var cellIndex = cell.dom.cellIndex;
6130 var rowIndex = row.dom.rowIndex - 1; // start from 0
6132 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6136 onMouseout : function(e, el)
6138 var cell = Roo.get(el);
6144 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6145 cell = cell.findParent('td', false, true);
6148 var row = cell.findParent('tr', false, true);
6149 var cellIndex = cell.dom.cellIndex;
6150 var rowIndex = row.dom.rowIndex - 1; // start from 0
6152 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6156 onClick : function(e, el)
6158 var cell = Roo.get(el);
6160 if(!cell || (!this.cellSelection && !this.rowSelection)){
6164 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6165 cell = cell.findParent('td', false, true);
6168 if(!cell || typeof(cell) == 'undefined'){
6172 var row = cell.findParent('tr', false, true);
6174 if(!row || typeof(row) == 'undefined'){
6178 var cellIndex = cell.dom.cellIndex;
6179 var rowIndex = this.getRowIndex(row);
6181 // why??? - should these not be based on SelectionModel?
6182 if(this.cellSelection){
6183 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6186 if(this.rowSelection){
6187 this.fireEvent('rowclick', this, row, rowIndex, e);
6193 onDblClick : function(e,el)
6195 var cell = Roo.get(el);
6197 if(!cell || (!this.cellSelection && !this.rowSelection)){
6201 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6202 cell = cell.findParent('td', false, true);
6205 if(!cell || typeof(cell) == 'undefined'){
6209 var row = cell.findParent('tr', false, true);
6211 if(!row || typeof(row) == 'undefined'){
6215 var cellIndex = cell.dom.cellIndex;
6216 var rowIndex = this.getRowIndex(row);
6218 if(this.cellSelection){
6219 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6222 if(this.rowSelection){
6223 this.fireEvent('rowdblclick', this, row, rowIndex, e);
6227 sort : function(e,el)
6229 var col = Roo.get(el);
6231 if(!col.hasClass('sortable')){
6235 var sort = col.attr('sort');
6238 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6242 this.store.sortInfo = {field : sort, direction : dir};
6245 Roo.log("calling footer first");
6246 this.footer.onClick('first');
6249 this.store.load({ params : { start : 0 } });
6253 renderHeader : function()
6261 this.totalWidth = 0;
6263 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6265 var config = cm.config[i];
6270 html: cm.getColumnHeader(i)
6275 if(typeof(config.sortable) != 'undefined' && config.sortable){
6277 c.html = '<i class="glyphicon"></i>' + c.html;
6280 if(typeof(config.lgHeader) != 'undefined'){
6281 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6284 if(typeof(config.mdHeader) != 'undefined'){
6285 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6288 if(typeof(config.smHeader) != 'undefined'){
6289 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6292 if(typeof(config.xsHeader) != 'undefined'){
6293 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6300 if(typeof(config.tooltip) != 'undefined'){
6301 c.tooltip = config.tooltip;
6304 if(typeof(config.colspan) != 'undefined'){
6305 c.colspan = config.colspan;
6308 if(typeof(config.hidden) != 'undefined' && config.hidden){
6309 c.style += ' display:none;';
6312 if(typeof(config.dataIndex) != 'undefined'){
6313 c.sort = config.dataIndex;
6318 if(typeof(config.align) != 'undefined' && config.align.length){
6319 c.style += ' text-align:' + config.align + ';';
6322 if(typeof(config.width) != 'undefined'){
6323 c.style += ' width:' + config.width + 'px;';
6324 this.totalWidth += config.width;
6326 this.totalWidth += 100; // assume minimum of 100 per column?
6329 if(typeof(config.cls) != 'undefined'){
6330 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6333 ['xs','sm','md','lg'].map(function(size){
6335 if(typeof(config[size]) == 'undefined'){
6339 if (!config[size]) { // 0 = hidden
6340 c.cls += ' hidden-' + size;
6344 c.cls += ' col-' + size + '-' + config[size];
6354 renderBody : function()
6364 colspan : this.cm.getColumnCount()
6374 renderFooter : function()
6384 colspan : this.cm.getColumnCount()
6398 // Roo.log('ds onload');
6403 var ds = this.store;
6405 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6406 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6407 if (_this.store.sortInfo) {
6409 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6410 e.select('i', true).addClass(['glyphicon-arrow-up']);
6413 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6414 e.select('i', true).addClass(['glyphicon-arrow-down']);
6419 var tbody = this.mainBody;
6421 if(ds.getCount() > 0){
6422 ds.data.each(function(d,rowIndex){
6423 var row = this.renderRow(cm, ds, rowIndex);
6425 tbody.createChild(row);
6429 if(row.cellObjects.length){
6430 Roo.each(row.cellObjects, function(r){
6431 _this.renderCellObject(r);
6438 Roo.each(this.el.select('tbody td', true).elements, function(e){
6439 e.on('mouseover', _this.onMouseover, _this);
6442 Roo.each(this.el.select('tbody td', true).elements, function(e){
6443 e.on('mouseout', _this.onMouseout, _this);
6445 this.fireEvent('rowsrendered', this);
6446 //if(this.loadMask){
6447 // this.maskEl.hide();
6454 onUpdate : function(ds,record)
6456 this.refreshRow(record);
6460 onRemove : function(ds, record, index, isUpdate){
6461 if(isUpdate !== true){
6462 this.fireEvent("beforerowremoved", this, index, record);
6464 var bt = this.mainBody.dom;
6466 var rows = this.el.select('tbody > tr', true).elements;
6468 if(typeof(rows[index]) != 'undefined'){
6469 bt.removeChild(rows[index].dom);
6472 // if(bt.rows[index]){
6473 // bt.removeChild(bt.rows[index]);
6476 if(isUpdate !== true){
6477 //this.stripeRows(index);
6478 //this.syncRowHeights(index, index);
6480 this.fireEvent("rowremoved", this, index, record);
6484 onAdd : function(ds, records, rowIndex)
6486 //Roo.log('on Add called');
6487 // - note this does not handle multiple adding very well..
6488 var bt = this.mainBody.dom;
6489 for (var i =0 ; i < records.length;i++) {
6490 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6491 //Roo.log(records[i]);
6492 //Roo.log(this.store.getAt(rowIndex+i));
6493 this.insertRow(this.store, rowIndex + i, false);
6500 refreshRow : function(record){
6501 var ds = this.store, index;
6502 if(typeof record == 'number'){
6504 record = ds.getAt(index);
6506 index = ds.indexOf(record);
6508 this.insertRow(ds, index, true);
6510 this.onRemove(ds, record, index+1, true);
6512 //this.syncRowHeights(index, index);
6514 this.fireEvent("rowupdated", this, index, record);
6517 insertRow : function(dm, rowIndex, isUpdate){
6520 this.fireEvent("beforerowsinserted", this, rowIndex);
6522 //var s = this.getScrollState();
6523 var row = this.renderRow(this.cm, this.store, rowIndex);
6524 // insert before rowIndex..
6525 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6529 if(row.cellObjects.length){
6530 Roo.each(row.cellObjects, function(r){
6531 _this.renderCellObject(r);
6536 this.fireEvent("rowsinserted", this, rowIndex);
6537 //this.syncRowHeights(firstRow, lastRow);
6538 //this.stripeRows(firstRow);
6545 getRowDom : function(rowIndex)
6547 var rows = this.el.select('tbody > tr', true).elements;
6549 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6552 // returns the object tree for a tr..
6555 renderRow : function(cm, ds, rowIndex)
6558 var d = ds.getAt(rowIndex);
6565 var cellObjects = [];
6567 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6568 var config = cm.config[i];
6570 var renderer = cm.getRenderer(i);
6574 if(typeof(renderer) !== 'undefined'){
6575 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6577 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6578 // and are rendered into the cells after the row is rendered - using the id for the element.
6580 if(typeof(value) === 'object'){
6590 rowIndex : rowIndex,
6595 this.fireEvent('rowclass', this, rowcfg);
6599 cls : rowcfg.rowClass,
6601 html: (typeof(value) === 'object') ? '' : value
6608 if(typeof(config.colspan) != 'undefined'){
6609 td.colspan = config.colspan;
6612 if(typeof(config.hidden) != 'undefined' && config.hidden){
6613 td.style += ' display:none;';
6616 if(typeof(config.align) != 'undefined' && config.align.length){
6617 td.style += ' text-align:' + config.align + ';';
6620 if(typeof(config.width) != 'undefined'){
6621 td.style += ' width:' + config.width + 'px;';
6624 if(typeof(config.cursor) != 'undefined'){
6625 td.style += ' cursor:' + config.cursor + ';';
6628 if(typeof(config.cls) != 'undefined'){
6629 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6632 ['xs','sm','md','lg'].map(function(size){
6634 if(typeof(config[size]) == 'undefined'){
6638 if (!config[size]) { // 0 = hidden
6639 td.cls += ' hidden-' + size;
6643 td.cls += ' col-' + size + '-' + config[size];
6651 row.cellObjects = cellObjects;
6659 onBeforeLoad : function()
6661 //Roo.log('ds onBeforeLoad');
6665 //if(this.loadMask){
6666 // this.maskEl.show();
6674 this.el.select('tbody', true).first().dom.innerHTML = '';
6677 * Show or hide a row.
6678 * @param {Number} rowIndex to show or hide
6679 * @param {Boolean} state hide
6681 setRowVisibility : function(rowIndex, state)
6683 var bt = this.mainBody.dom;
6685 var rows = this.el.select('tbody > tr', true).elements;
6687 if(typeof(rows[rowIndex]) == 'undefined'){
6690 rows[rowIndex].dom.style.display = state ? '' : 'none';
6694 getSelectionModel : function(){
6696 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6698 return this.selModel;
6701 * Render the Roo.bootstrap object from renderder
6703 renderCellObject : function(r)
6707 var t = r.cfg.render(r.container);
6710 Roo.each(r.cfg.cn, function(c){
6712 container: t.getChildContainer(),
6715 _this.renderCellObject(child);
6720 getRowIndex : function(row)
6724 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6735 * Returns the grid's underlying element = used by panel.Grid
6736 * @return {Element} The element
6738 getGridEl : function(){
6742 * Forces a resize - used by panel.Grid
6743 * @return {Element} The element
6745 autoSize : function()
6747 //var ctr = Roo.get(this.container.dom.parentElement);
6748 var ctr = Roo.get(this.el.dom);
6750 var thd = this.getGridEl().select('thead',true).first();
6751 var tbd = this.getGridEl().select('tbody', true).first();
6752 var tfd = this.getGridEl().select('tfoot', true).first();
6754 var cw = ctr.getWidth();
6758 tbd.setSize(ctr.getWidth(),
6759 ctr.getHeight() - (thd.getHeight() + (tfd ? tfd.getHeight() : 0))
6761 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6764 cw = Math.max(cw, this.totalWidth);
6765 this.getGridEl().select('tr',true).setWidth(cw);
6766 // resize 'expandable coloumn?
6768 return; // we doe not have a view in this design..
6771 onBodyScroll: function()
6774 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6775 this.mainHead.setStyle({
6776 'position' : 'relative',
6777 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6794 * @class Roo.bootstrap.TableCell
6795 * @extends Roo.bootstrap.Component
6796 * Bootstrap TableCell class
6797 * @cfg {String} html cell contain text
6798 * @cfg {String} cls cell class
6799 * @cfg {String} tag cell tag (td|th) default td
6800 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6801 * @cfg {String} align Aligns the content in a cell
6802 * @cfg {String} axis Categorizes cells
6803 * @cfg {String} bgcolor Specifies the background color of a cell
6804 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6805 * @cfg {Number} colspan Specifies the number of columns a cell should span
6806 * @cfg {String} headers Specifies one or more header cells a cell is related to
6807 * @cfg {Number} height Sets the height of a cell
6808 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6809 * @cfg {Number} rowspan Sets the number of rows a cell should span
6810 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6811 * @cfg {String} valign Vertical aligns the content in a cell
6812 * @cfg {Number} width Specifies the width of a cell
6815 * Create a new TableCell
6816 * @param {Object} config The config object
6819 Roo.bootstrap.TableCell = function(config){
6820 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6823 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
6843 getAutoCreate : function(){
6844 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6864 cfg.align=this.align
6870 cfg.bgcolor=this.bgcolor
6873 cfg.charoff=this.charoff
6876 cfg.colspan=this.colspan
6879 cfg.headers=this.headers
6882 cfg.height=this.height
6885 cfg.nowrap=this.nowrap
6888 cfg.rowspan=this.rowspan
6891 cfg.scope=this.scope
6894 cfg.valign=this.valign
6897 cfg.width=this.width
6916 * @class Roo.bootstrap.TableRow
6917 * @extends Roo.bootstrap.Component
6918 * Bootstrap TableRow class
6919 * @cfg {String} cls row class
6920 * @cfg {String} align Aligns the content in a table row
6921 * @cfg {String} bgcolor Specifies a background color for a table row
6922 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6923 * @cfg {String} valign Vertical aligns the content in a table row
6926 * Create a new TableRow
6927 * @param {Object} config The config object
6930 Roo.bootstrap.TableRow = function(config){
6931 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6934 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
6942 getAutoCreate : function(){
6943 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6953 cfg.align = this.align;
6956 cfg.bgcolor = this.bgcolor;
6959 cfg.charoff = this.charoff;
6962 cfg.valign = this.valign;
6980 * @class Roo.bootstrap.TableBody
6981 * @extends Roo.bootstrap.Component
6982 * Bootstrap TableBody class
6983 * @cfg {String} cls element class
6984 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6985 * @cfg {String} align Aligns the content inside the element
6986 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6987 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6990 * Create a new TableBody
6991 * @param {Object} config The config object
6994 Roo.bootstrap.TableBody = function(config){
6995 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6998 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
7006 getAutoCreate : function(){
7007 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7021 cfg.align = this.align;
7024 cfg.charoff = this.charoff;
7027 cfg.valign = this.valign;
7034 // initEvents : function()
7041 // this.store = Roo.factory(this.store, Roo.data);
7042 // this.store.on('load', this.onLoad, this);
7044 // this.store.load();
7048 // onLoad: function ()
7050 // this.fireEvent('load', this);
7060 * Ext JS Library 1.1.1
7061 * Copyright(c) 2006-2007, Ext JS, LLC.
7063 * Originally Released Under LGPL - original licence link has changed is not relivant.
7066 * <script type="text/javascript">
7069 // as we use this in bootstrap.
7070 Roo.namespace('Roo.form');
7072 * @class Roo.form.Action
7073 * Internal Class used to handle form actions
7075 * @param {Roo.form.BasicForm} el The form element or its id
7076 * @param {Object} config Configuration options
7081 // define the action interface
7082 Roo.form.Action = function(form, options){
7084 this.options = options || {};
7087 * Client Validation Failed
7090 Roo.form.Action.CLIENT_INVALID = 'client';
7092 * Server Validation Failed
7095 Roo.form.Action.SERVER_INVALID = 'server';
7097 * Connect to Server Failed
7100 Roo.form.Action.CONNECT_FAILURE = 'connect';
7102 * Reading Data from Server Failed
7105 Roo.form.Action.LOAD_FAILURE = 'load';
7107 Roo.form.Action.prototype = {
7109 failureType : undefined,
7110 response : undefined,
7114 run : function(options){
7119 success : function(response){
7124 handleResponse : function(response){
7128 // default connection failure
7129 failure : function(response){
7131 this.response = response;
7132 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7133 this.form.afterAction(this, false);
7136 processResponse : function(response){
7137 this.response = response;
7138 if(!response.responseText){
7141 this.result = this.handleResponse(response);
7145 // utility functions used internally
7146 getUrl : function(appendParams){
7147 var url = this.options.url || this.form.url || this.form.el.dom.action;
7149 var p = this.getParams();
7151 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7157 getMethod : function(){
7158 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7161 getParams : function(){
7162 var bp = this.form.baseParams;
7163 var p = this.options.params;
7165 if(typeof p == "object"){
7166 p = Roo.urlEncode(Roo.applyIf(p, bp));
7167 }else if(typeof p == 'string' && bp){
7168 p += '&' + Roo.urlEncode(bp);
7171 p = Roo.urlEncode(bp);
7176 createCallback : function(){
7178 success: this.success,
7179 failure: this.failure,
7181 timeout: (this.form.timeout*1000),
7182 upload: this.form.fileUpload ? this.success : undefined
7187 Roo.form.Action.Submit = function(form, options){
7188 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7191 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7194 haveProgress : false,
7195 uploadComplete : false,
7197 // uploadProgress indicator.
7198 uploadProgress : function()
7200 if (!this.form.progressUrl) {
7204 if (!this.haveProgress) {
7205 Roo.MessageBox.progress("Uploading", "Uploading");
7207 if (this.uploadComplete) {
7208 Roo.MessageBox.hide();
7212 this.haveProgress = true;
7214 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7216 var c = new Roo.data.Connection();
7218 url : this.form.progressUrl,
7223 success : function(req){
7224 //console.log(data);
7228 rdata = Roo.decode(req.responseText)
7230 Roo.log("Invalid data from server..");
7234 if (!rdata || !rdata.success) {
7236 Roo.MessageBox.alert(Roo.encode(rdata));
7239 var data = rdata.data;
7241 if (this.uploadComplete) {
7242 Roo.MessageBox.hide();
7247 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7248 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7251 this.uploadProgress.defer(2000,this);
7254 failure: function(data) {
7255 Roo.log('progress url failed ');
7266 // run get Values on the form, so it syncs any secondary forms.
7267 this.form.getValues();
7269 var o = this.options;
7270 var method = this.getMethod();
7271 var isPost = method == 'POST';
7272 if(o.clientValidation === false || this.form.isValid()){
7274 if (this.form.progressUrl) {
7275 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7276 (new Date() * 1) + '' + Math.random());
7281 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7282 form:this.form.el.dom,
7283 url:this.getUrl(!isPost),
7285 params:isPost ? this.getParams() : null,
7286 isUpload: this.form.fileUpload
7289 this.uploadProgress();
7291 }else if (o.clientValidation !== false){ // client validation failed
7292 this.failureType = Roo.form.Action.CLIENT_INVALID;
7293 this.form.afterAction(this, false);
7297 success : function(response)
7299 this.uploadComplete= true;
7300 if (this.haveProgress) {
7301 Roo.MessageBox.hide();
7305 var result = this.processResponse(response);
7306 if(result === true || result.success){
7307 this.form.afterAction(this, true);
7311 this.form.markInvalid(result.errors);
7312 this.failureType = Roo.form.Action.SERVER_INVALID;
7314 this.form.afterAction(this, false);
7316 failure : function(response)
7318 this.uploadComplete= true;
7319 if (this.haveProgress) {
7320 Roo.MessageBox.hide();
7323 this.response = response;
7324 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7325 this.form.afterAction(this, false);
7328 handleResponse : function(response){
7329 if(this.form.errorReader){
7330 var rs = this.form.errorReader.read(response);
7333 for(var i = 0, len = rs.records.length; i < len; i++) {
7334 var r = rs.records[i];
7338 if(errors.length < 1){
7342 success : rs.success,
7348 ret = Roo.decode(response.responseText);
7352 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7362 Roo.form.Action.Load = function(form, options){
7363 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7364 this.reader = this.form.reader;
7367 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7372 Roo.Ajax.request(Roo.apply(
7373 this.createCallback(), {
7374 method:this.getMethod(),
7375 url:this.getUrl(false),
7376 params:this.getParams()
7380 success : function(response){
7382 var result = this.processResponse(response);
7383 if(result === true || !result.success || !result.data){
7384 this.failureType = Roo.form.Action.LOAD_FAILURE;
7385 this.form.afterAction(this, false);
7388 this.form.clearInvalid();
7389 this.form.setValues(result.data);
7390 this.form.afterAction(this, true);
7393 handleResponse : function(response){
7394 if(this.form.reader){
7395 var rs = this.form.reader.read(response);
7396 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7398 success : rs.success,
7402 return Roo.decode(response.responseText);
7406 Roo.form.Action.ACTION_TYPES = {
7407 'load' : Roo.form.Action.Load,
7408 'submit' : Roo.form.Action.Submit
7417 * @class Roo.bootstrap.Form
7418 * @extends Roo.bootstrap.Component
7419 * Bootstrap Form class
7420 * @cfg {String} method GET | POST (default POST)
7421 * @cfg {String} labelAlign top | left (default top)
7422 * @cfg {String} align left | right - for navbars
7423 * @cfg {Boolean} loadMask load mask when submit (default true)
7428 * @param {Object} config The config object
7432 Roo.bootstrap.Form = function(config){
7433 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7436 * @event clientvalidation
7437 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7438 * @param {Form} this
7439 * @param {Boolean} valid true if the form has passed client-side validation
7441 clientvalidation: true,
7443 * @event beforeaction
7444 * Fires before any action is performed. Return false to cancel the action.
7445 * @param {Form} this
7446 * @param {Action} action The action to be performed
7450 * @event actionfailed
7451 * Fires when an action fails.
7452 * @param {Form} this
7453 * @param {Action} action The action that failed
7455 actionfailed : true,
7457 * @event actioncomplete
7458 * Fires when an action is completed.
7459 * @param {Form} this
7460 * @param {Action} action The action that completed
7462 actioncomplete : true
7467 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7470 * @cfg {String} method
7471 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7476 * The URL to use for form actions if one isn't supplied in the action options.
7479 * @cfg {Boolean} fileUpload
7480 * Set to true if this form is a file upload.
7484 * @cfg {Object} baseParams
7485 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7489 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7493 * @cfg {Sting} align (left|right) for navbar forms
7498 activeAction : null,
7501 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7502 * element by passing it or its id or mask the form itself by passing in true.
7505 waitMsgTarget : false,
7509 getAutoCreate : function(){
7513 method : this.method || 'POST',
7514 id : this.id || Roo.id(),
7517 if (this.parent().xtype.match(/^Nav/)) {
7518 cfg.cls = 'navbar-form navbar-' + this.align;
7522 if (this.labelAlign == 'left' ) {
7523 cfg.cls += ' form-horizontal';
7529 initEvents : function()
7531 this.el.on('submit', this.onSubmit, this);
7532 // this was added as random key presses on the form where triggering form submit.
7533 this.el.on('keypress', function(e) {
7534 if (e.getCharCode() != 13) {
7537 // we might need to allow it for textareas.. and some other items.
7538 // check e.getTarget().
7540 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7544 Roo.log("keypress blocked");
7552 onSubmit : function(e){
7557 * Returns true if client-side validation on the form is successful.
7560 isValid : function(){
7561 var items = this.getItems();
7563 items.each(function(f){
7572 * Returns true if any fields in this form have changed since their original load.
7575 isDirty : function(){
7577 var items = this.getItems();
7578 items.each(function(f){
7588 * Performs a predefined action (submit or load) or custom actions you define on this form.
7589 * @param {String} actionName The name of the action type
7590 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
7591 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7592 * accept other config options):
7594 Property Type Description
7595 ---------------- --------------- ----------------------------------------------------------------------------------
7596 url String The url for the action (defaults to the form's url)
7597 method String The form method to use (defaults to the form's method, or POST if not defined)
7598 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
7599 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
7600 validate the form on the client (defaults to false)
7602 * @return {BasicForm} this
7604 doAction : function(action, options){
7605 if(typeof action == 'string'){
7606 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7608 if(this.fireEvent('beforeaction', this, action) !== false){
7609 this.beforeAction(action);
7610 action.run.defer(100, action);
7616 beforeAction : function(action){
7617 var o = action.options;
7620 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7622 // not really supported yet.. ??
7624 //if(this.waitMsgTarget === true){
7625 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7626 //}else if(this.waitMsgTarget){
7627 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7628 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7630 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7636 afterAction : function(action, success){
7637 this.activeAction = null;
7638 var o = action.options;
7640 //if(this.waitMsgTarget === true){
7642 //}else if(this.waitMsgTarget){
7643 // this.waitMsgTarget.unmask();
7645 // Roo.MessageBox.updateProgress(1);
7646 // Roo.MessageBox.hide();
7653 Roo.callback(o.success, o.scope, [this, action]);
7654 this.fireEvent('actioncomplete', this, action);
7658 // failure condition..
7659 // we have a scenario where updates need confirming.
7660 // eg. if a locking scenario exists..
7661 // we look for { errors : { needs_confirm : true }} in the response.
7663 (typeof(action.result) != 'undefined') &&
7664 (typeof(action.result.errors) != 'undefined') &&
7665 (typeof(action.result.errors.needs_confirm) != 'undefined')
7668 Roo.log("not supported yet");
7671 Roo.MessageBox.confirm(
7672 "Change requires confirmation",
7673 action.result.errorMsg,
7678 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
7688 Roo.callback(o.failure, o.scope, [this, action]);
7689 // show an error message if no failed handler is set..
7690 if (!this.hasListener('actionfailed')) {
7691 Roo.log("need to add dialog support");
7693 Roo.MessageBox.alert("Error",
7694 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7695 action.result.errorMsg :
7696 "Saving Failed, please check your entries or try again"
7701 this.fireEvent('actionfailed', this, action);
7706 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7707 * @param {String} id The value to search for
7710 findField : function(id){
7711 var items = this.getItems();
7712 var field = items.get(id);
7714 items.each(function(f){
7715 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7722 return field || null;
7725 * Mark fields in this form invalid in bulk.
7726 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7727 * @return {BasicForm} this
7729 markInvalid : function(errors){
7730 if(errors instanceof Array){
7731 for(var i = 0, len = errors.length; i < len; i++){
7732 var fieldError = errors[i];
7733 var f = this.findField(fieldError.id);
7735 f.markInvalid(fieldError.msg);
7741 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7742 field.markInvalid(errors[id]);
7746 //Roo.each(this.childForms || [], function (f) {
7747 // f.markInvalid(errors);
7754 * Set values for fields in this form in bulk.
7755 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7756 * @return {BasicForm} this
7758 setValues : function(values){
7759 if(values instanceof Array){ // array of objects
7760 for(var i = 0, len = values.length; i < len; i++){
7762 var f = this.findField(v.id);
7764 f.setValue(v.value);
7765 if(this.trackResetOnLoad){
7766 f.originalValue = f.getValue();
7770 }else{ // object hash
7773 if(typeof values[id] != 'function' && (field = this.findField(id))){
7775 if (field.setFromData &&
7777 field.displayField &&
7778 // combos' with local stores can
7779 // be queried via setValue()
7780 // to set their value..
7781 (field.store && !field.store.isLocal)
7785 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7786 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7787 field.setFromData(sd);
7790 field.setValue(values[id]);
7794 if(this.trackResetOnLoad){
7795 field.originalValue = field.getValue();
7801 //Roo.each(this.childForms || [], function (f) {
7802 // f.setValues(values);
7809 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7810 * they are returned as an array.
7811 * @param {Boolean} asString
7814 getValues : function(asString){
7815 //if (this.childForms) {
7816 // copy values from the child forms
7817 // Roo.each(this.childForms, function (f) {
7818 // this.setValues(f.getValues());
7824 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7825 if(asString === true){
7828 return Roo.urlDecode(fs);
7832 * Returns the fields in this form as an object with key/value pairs.
7833 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7836 getFieldValues : function(with_hidden)
7838 var items = this.getItems();
7840 items.each(function(f){
7844 var v = f.getValue();
7845 if (f.inputType =='radio') {
7846 if (typeof(ret[f.getName()]) == 'undefined') {
7847 ret[f.getName()] = ''; // empty..
7850 if (!f.el.dom.checked) {
7858 // not sure if this supported any more..
7859 if ((typeof(v) == 'object') && f.getRawValue) {
7860 v = f.getRawValue() ; // dates..
7862 // combo boxes where name != hiddenName...
7863 if (f.name != f.getName()) {
7864 ret[f.name] = f.getRawValue();
7866 ret[f.getName()] = v;
7873 * Clears all invalid messages in this form.
7874 * @return {BasicForm} this
7876 clearInvalid : function(){
7877 var items = this.getItems();
7879 items.each(function(f){
7890 * @return {BasicForm} this
7893 var items = this.getItems();
7894 items.each(function(f){
7898 Roo.each(this.childForms || [], function (f) {
7905 getItems : function()
7907 var r=new Roo.util.MixedCollection(false, function(o){
7908 return o.id || (o.id = Roo.id());
7910 var iter = function(el) {
7917 Roo.each(el.items,function(e) {
7935 * Ext JS Library 1.1.1
7936 * Copyright(c) 2006-2007, Ext JS, LLC.
7938 * Originally Released Under LGPL - original licence link has changed is not relivant.
7941 * <script type="text/javascript">
7944 * @class Roo.form.VTypes
7945 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7948 Roo.form.VTypes = function(){
7949 // closure these in so they are only created once.
7950 var alpha = /^[a-zA-Z_]+$/;
7951 var alphanum = /^[a-zA-Z0-9_]+$/;
7952 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
7953 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7955 // All these messages and functions are configurable
7958 * The function used to validate email addresses
7959 * @param {String} value The email address
7961 'email' : function(v){
7962 return email.test(v);
7965 * The error text to display when the email validation function returns false
7968 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7970 * The keystroke filter mask to be applied on email input
7973 'emailMask' : /[a-z0-9_\.\-@]/i,
7976 * The function used to validate URLs
7977 * @param {String} value The URL
7979 'url' : function(v){
7983 * The error text to display when the url validation function returns false
7986 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7989 * The function used to validate alpha values
7990 * @param {String} value The value
7992 'alpha' : function(v){
7993 return alpha.test(v);
7996 * The error text to display when the alpha validation function returns false
7999 'alphaText' : 'This field should only contain letters and _',
8001 * The keystroke filter mask to be applied on alpha input
8004 'alphaMask' : /[a-z_]/i,
8007 * The function used to validate alphanumeric values
8008 * @param {String} value The value
8010 'alphanum' : function(v){
8011 return alphanum.test(v);
8014 * The error text to display when the alphanumeric validation function returns false
8017 'alphanumText' : 'This field should only contain letters, numbers and _',
8019 * The keystroke filter mask to be applied on alphanumeric input
8022 'alphanumMask' : /[a-z0-9_]/i
8032 * @class Roo.bootstrap.Input
8033 * @extends Roo.bootstrap.Component
8034 * Bootstrap Input class
8035 * @cfg {Boolean} disabled is it disabled
8036 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8037 * @cfg {String} name name of the input
8038 * @cfg {string} fieldLabel - the label associated
8039 * @cfg {string} placeholder - placeholder to put in text.
8040 * @cfg {string} before - input group add on before
8041 * @cfg {string} after - input group add on after
8042 * @cfg {string} size - (lg|sm) or leave empty..
8043 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8044 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8045 * @cfg {Number} md colspan out of 12 for computer-sized screens
8046 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8047 * @cfg {string} value default value of the input
8048 * @cfg {Number} labelWidth set the width of label (0-12)
8049 * @cfg {String} labelAlign (top|left)
8050 * @cfg {Boolean} readOnly Specifies that the field should be read-only
8051 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8052 * @cfg {String} indicatorpos (left|right) default left
8054 * @cfg {String} align (left|center|right) Default left
8055 * @cfg {Boolean} forceFeedback (true|false) Default false
8061 * Create a new Input
8062 * @param {Object} config The config object
8065 Roo.bootstrap.Input = function(config){
8066 Roo.bootstrap.Input.superclass.constructor.call(this, config);
8071 * Fires when this field receives input focus.
8072 * @param {Roo.form.Field} this
8077 * Fires when this field loses input focus.
8078 * @param {Roo.form.Field} this
8083 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8084 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8085 * @param {Roo.form.Field} this
8086 * @param {Roo.EventObject} e The event object
8091 * Fires just before the field blurs if the field value has changed.
8092 * @param {Roo.form.Field} this
8093 * @param {Mixed} newValue The new value
8094 * @param {Mixed} oldValue The original value
8099 * Fires after the field has been marked as invalid.
8100 * @param {Roo.form.Field} this
8101 * @param {String} msg The validation message
8106 * Fires after the field has been validated with no errors.
8107 * @param {Roo.form.Field} this
8112 * Fires after the key up
8113 * @param {Roo.form.Field} this
8114 * @param {Roo.EventObject} e The event Object
8120 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
8122 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8123 automatic validation (defaults to "keyup").
8125 validationEvent : "keyup",
8127 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8129 validateOnBlur : true,
8131 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8133 validationDelay : 250,
8135 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8137 focusClass : "x-form-focus", // not needed???
8141 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8143 invalidClass : "has-warning",
8146 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8148 validClass : "has-success",
8151 * @cfg {Boolean} hasFeedback (true|false) default true
8156 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8158 invalidFeedbackClass : "glyphicon-warning-sign",
8161 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8163 validFeedbackClass : "glyphicon-ok",
8166 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8168 selectOnFocus : false,
8171 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8175 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8180 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8182 disableKeyFilter : false,
8185 * @cfg {Boolean} disabled True to disable the field (defaults to false).
8189 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8193 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8195 blankText : "This field is required",
8198 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8202 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8204 maxLength : Number.MAX_VALUE,
8206 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8208 minLengthText : "The minimum length for this field is {0}",
8210 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8212 maxLengthText : "The maximum length for this field is {0}",
8216 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8217 * If available, this function will be called only after the basic validators all return true, and will be passed the
8218 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8222 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8223 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8224 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
8228 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
8232 autocomplete: false,
8251 formatedValue : false,
8252 forceFeedback : false,
8254 indicatorpos : 'left',
8256 parentLabelAlign : function()
8259 while (parent.parent()) {
8260 parent = parent.parent();
8261 if (typeof(parent.labelAlign) !='undefined') {
8262 return parent.labelAlign;
8269 getAutoCreate : function()
8271 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8277 if(this.inputType != 'hidden'){
8278 cfg.cls = 'form-group' //input-group
8284 type : this.inputType,
8286 cls : 'form-control',
8287 placeholder : this.placeholder || '',
8288 autocomplete : this.autocomplete || 'new-password'
8292 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8295 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8296 input.maxLength = this.maxLength;
8299 if (this.disabled) {
8300 input.disabled=true;
8303 if (this.readOnly) {
8304 input.readonly=true;
8308 input.name = this.name;
8312 input.cls += ' input-' + this.size;
8316 ['xs','sm','md','lg'].map(function(size){
8317 if (settings[size]) {
8318 cfg.cls += ' col-' + size + '-' + settings[size];
8322 var inputblock = input;
8326 cls: 'glyphicon form-control-feedback'
8329 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8332 cls : 'has-feedback',
8340 if (this.before || this.after) {
8343 cls : 'input-group',
8347 if (this.before && typeof(this.before) == 'string') {
8349 inputblock.cn.push({
8351 cls : 'roo-input-before input-group-addon',
8355 if (this.before && typeof(this.before) == 'object') {
8356 this.before = Roo.factory(this.before);
8358 inputblock.cn.push({
8360 cls : 'roo-input-before input-group-' +
8361 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8365 inputblock.cn.push(input);
8367 if (this.after && typeof(this.after) == 'string') {
8368 inputblock.cn.push({
8370 cls : 'roo-input-after input-group-addon',
8374 if (this.after && typeof(this.after) == 'object') {
8375 this.after = Roo.factory(this.after);
8377 inputblock.cn.push({
8379 cls : 'roo-input-after input-group-' +
8380 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8384 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8385 inputblock.cls += ' has-feedback';
8386 inputblock.cn.push(feedback);
8390 if (align ==='left' && this.fieldLabel.length) {
8395 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8396 tooltip : 'This field is required'
8401 cls : 'control-label col-sm-' + this.labelWidth,
8402 html : this.fieldLabel
8406 cls : "col-sm-" + (12 - this.labelWidth),
8414 if(this.indicatorpos == 'right'){
8419 cls : 'control-label col-sm-' + this.labelWidth,
8420 html : this.fieldLabel
8425 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8426 tooltip : 'This field is required'
8429 cls : "col-sm-" + (12 - this.labelWidth),
8438 } else if ( this.fieldLabel.length) {
8443 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8444 tooltip : 'This field is required'
8448 //cls : 'input-group-addon',
8449 html : this.fieldLabel
8457 if(this.indicatorpos == 'right'){
8462 //cls : 'input-group-addon',
8463 html : this.fieldLabel
8468 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8469 tooltip : 'This field is required'
8489 if (this.parentType === 'Navbar' && this.parent().bar) {
8490 cfg.cls += ' navbar-form';
8493 if (this.parentType === 'NavGroup') {
8494 cfg.cls += ' navbar-form';
8502 * return the real input element.
8504 inputEl: function ()
8506 return this.el.select('input.form-control',true).first();
8509 tooltipEl : function()
8511 return this.inputEl();
8514 indicatorEl : function()
8516 var indicator = this.el.select('i.roo-required-indicator',true).first();
8526 setDisabled : function(v)
8528 var i = this.inputEl().dom;
8530 i.removeAttribute('disabled');
8534 i.setAttribute('disabled','true');
8536 initEvents : function()
8539 this.inputEl().on("keydown" , this.fireKey, this);
8540 this.inputEl().on("focus", this.onFocus, this);
8541 this.inputEl().on("blur", this.onBlur, this);
8543 this.inputEl().relayEvent('keyup', this);
8545 this.indicator = this.indicatorEl();
8548 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
8549 this.indicator.hide();
8552 // reference to original value for reset
8553 this.originalValue = this.getValue();
8554 //Roo.form.TextField.superclass.initEvents.call(this);
8555 if(this.validationEvent == 'keyup'){
8556 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8557 this.inputEl().on('keyup', this.filterValidation, this);
8559 else if(this.validationEvent !== false){
8560 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8563 if(this.selectOnFocus){
8564 this.on("focus", this.preFocus, this);
8567 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8568 this.inputEl().on("keypress", this.filterKeys, this);
8570 this.inputEl().relayEvent('keypress', this);
8573 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
8574 this.el.on("click", this.autoSize, this);
8577 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8578 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8581 if (typeof(this.before) == 'object') {
8582 this.before.render(this.el.select('.roo-input-before',true).first());
8584 if (typeof(this.after) == 'object') {
8585 this.after.render(this.el.select('.roo-input-after',true).first());
8590 filterValidation : function(e){
8591 if(!e.isNavKeyPress()){
8592 this.validationTask.delay(this.validationDelay);
8596 * Validates the field value
8597 * @return {Boolean} True if the value is valid, else false
8599 validate : function(){
8600 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8601 if(this.disabled || this.validateValue(this.getRawValue())){
8612 * Validates a value according to the field's validation rules and marks the field as invalid
8613 * if the validation fails
8614 * @param {Mixed} value The value to validate
8615 * @return {Boolean} True if the value is valid, else false
8617 validateValue : function(value){
8618 if(value.length < 1) { // if it's blank
8619 if(this.allowBlank){
8625 if(value.length < this.minLength){
8628 if(value.length > this.maxLength){
8632 var vt = Roo.form.VTypes;
8633 if(!vt[this.vtype](value, this)){
8637 if(typeof this.validator == "function"){
8638 var msg = this.validator(value);
8644 if(this.regex && !this.regex.test(value)){
8654 fireKey : function(e){
8655 //Roo.log('field ' + e.getKey());
8656 if(e.isNavKeyPress()){
8657 this.fireEvent("specialkey", this, e);
8660 focus : function (selectText){
8662 this.inputEl().focus();
8663 if(selectText === true){
8664 this.inputEl().dom.select();
8670 onFocus : function(){
8671 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8672 // this.el.addClass(this.focusClass);
8675 this.hasFocus = true;
8676 this.startValue = this.getValue();
8677 this.fireEvent("focus", this);
8681 beforeBlur : Roo.emptyFn,
8685 onBlur : function(){
8687 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8688 //this.el.removeClass(this.focusClass);
8690 this.hasFocus = false;
8691 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8694 var v = this.getValue();
8695 if(String(v) !== String(this.startValue)){
8696 this.fireEvent('change', this, v, this.startValue);
8698 this.fireEvent("blur", this);
8702 * Resets the current field value to the originally loaded value and clears any validation messages
8705 this.setValue(this.originalValue);
8709 * Returns the name of the field
8710 * @return {Mixed} name The name field
8712 getName: function(){
8716 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
8717 * @return {Mixed} value The field value
8719 getValue : function(){
8721 var v = this.inputEl().getValue();
8726 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
8727 * @return {Mixed} value The field value
8729 getRawValue : function(){
8730 var v = this.inputEl().getValue();
8736 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
8737 * @param {Mixed} value The value to set
8739 setRawValue : function(v){
8740 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8743 selectText : function(start, end){
8744 var v = this.getRawValue();
8746 start = start === undefined ? 0 : start;
8747 end = end === undefined ? v.length : end;
8748 var d = this.inputEl().dom;
8749 if(d.setSelectionRange){
8750 d.setSelectionRange(start, end);
8751 }else if(d.createTextRange){
8752 var range = d.createTextRange();
8753 range.moveStart("character", start);
8754 range.moveEnd("character", v.length-end);
8761 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
8762 * @param {Mixed} value The value to set
8764 setValue : function(v){
8767 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8773 processValue : function(value){
8774 if(this.stripCharsRe){
8775 var newValue = value.replace(this.stripCharsRe, '');
8776 if(newValue !== value){
8777 this.setRawValue(newValue);
8784 preFocus : function(){
8786 if(this.selectOnFocus){
8787 this.inputEl().dom.select();
8790 filterKeys : function(e){
8792 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8795 var c = e.getCharCode(), cc = String.fromCharCode(c);
8796 if(Roo.isIE && (e.isSpecialKey() || !cc)){
8799 if(!this.maskRe.test(cc)){
8804 * Clear any invalid styles/messages for this field
8806 clearInvalid : function(){
8808 if(!this.el || this.preventMark){ // not rendered
8813 this.indicator.hide();
8816 this.el.removeClass(this.invalidClass);
8818 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8820 var feedback = this.el.select('.form-control-feedback', true).first();
8823 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8828 this.fireEvent('valid', this);
8832 * Mark this field as valid
8834 markValid : function()
8836 if(!this.el || this.preventMark){ // not rendered
8840 this.el.removeClass([this.invalidClass, this.validClass]);
8842 var feedback = this.el.select('.form-control-feedback', true).first();
8845 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8848 if(this.disabled || this.allowBlank){
8853 this.indicator.hide();
8856 this.el.addClass(this.validClass);
8858 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8860 var feedback = this.el.select('.form-control-feedback', true).first();
8863 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8864 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8869 this.fireEvent('valid', this);
8873 * Mark this field as invalid
8874 * @param {String} msg The validation message
8876 markInvalid : function(msg)
8878 if(!this.el || this.preventMark){ // not rendered
8882 this.el.removeClass([this.invalidClass, this.validClass]);
8884 var feedback = this.el.select('.form-control-feedback', true).first();
8887 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8890 if(this.disabled || this.allowBlank){
8895 this.indicator.show();
8898 this.el.addClass(this.invalidClass);
8900 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8902 var feedback = this.el.select('.form-control-feedback', true).first();
8905 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8907 if(this.getValue().length || this.forceFeedback){
8908 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8915 this.fireEvent('invalid', this, msg);
8918 SafariOnKeyDown : function(event)
8920 // this is a workaround for a password hang bug on chrome/ webkit.
8921 if (this.inputEl().dom.type != 'password') {
8925 var isSelectAll = false;
8927 if(this.inputEl().dom.selectionEnd > 0){
8928 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8930 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8931 event.preventDefault();
8936 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
8938 event.preventDefault();
8939 // this is very hacky as keydown always get's upper case.
8941 var cc = String.fromCharCode(event.getCharCode());
8942 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
8946 adjustWidth : function(tag, w){
8947 tag = tag.toLowerCase();
8948 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8949 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8953 if(tag == 'textarea'){
8956 }else if(Roo.isOpera){
8960 if(tag == 'textarea'){
8979 * @class Roo.bootstrap.TextArea
8980 * @extends Roo.bootstrap.Input
8981 * Bootstrap TextArea class
8982 * @cfg {Number} cols Specifies the visible width of a text area
8983 * @cfg {Number} rows Specifies the visible number of lines in a text area
8984 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8985 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8986 * @cfg {string} html text
8989 * Create a new TextArea
8990 * @param {Object} config The config object
8993 Roo.bootstrap.TextArea = function(config){
8994 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8998 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
9008 getAutoCreate : function(){
9010 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9021 value : this.value || '',
9022 html: this.html || '',
9023 cls : 'form-control',
9024 placeholder : this.placeholder || ''
9028 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9029 input.maxLength = this.maxLength;
9033 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9037 input.cols = this.cols;
9040 if (this.readOnly) {
9041 input.readonly = true;
9045 input.name = this.name;
9049 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9053 ['xs','sm','md','lg'].map(function(size){
9054 if (settings[size]) {
9055 cfg.cls += ' col-' + size + '-' + settings[size];
9059 var inputblock = input;
9061 if(this.hasFeedback && !this.allowBlank){
9065 cls: 'glyphicon form-control-feedback'
9069 cls : 'has-feedback',
9078 if (this.before || this.after) {
9081 cls : 'input-group',
9085 inputblock.cn.push({
9087 cls : 'input-group-addon',
9092 inputblock.cn.push(input);
9094 if(this.hasFeedback && !this.allowBlank){
9095 inputblock.cls += ' has-feedback';
9096 inputblock.cn.push(feedback);
9100 inputblock.cn.push({
9102 cls : 'input-group-addon',
9109 if (align ==='left' && this.fieldLabel.length) {
9110 // Roo.log("left and has label");
9116 cls : 'control-label col-sm-' + this.labelWidth,
9117 html : this.fieldLabel
9121 cls : "col-sm-" + (12 - this.labelWidth),
9128 } else if ( this.fieldLabel.length) {
9129 // Roo.log(" label");
9134 //cls : 'input-group-addon',
9135 html : this.fieldLabel
9145 // Roo.log(" no label && no align");
9155 if (this.disabled) {
9156 input.disabled=true;
9163 * return the real textarea element.
9165 inputEl: function ()
9167 return this.el.select('textarea.form-control',true).first();
9171 * Clear any invalid styles/messages for this field
9173 clearInvalid : function()
9176 if(!this.el || this.preventMark){ // not rendered
9180 var label = this.el.select('label', true).first();
9181 var icon = this.el.select('i.fa-star', true).first();
9187 this.el.removeClass(this.invalidClass);
9189 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9191 var feedback = this.el.select('.form-control-feedback', true).first();
9194 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9199 this.fireEvent('valid', this);
9203 * Mark this field as valid
9205 markValid : function()
9207 if(!this.el || this.preventMark){ // not rendered
9211 this.el.removeClass([this.invalidClass, this.validClass]);
9213 var feedback = this.el.select('.form-control-feedback', true).first();
9216 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9219 if(this.disabled || this.allowBlank){
9223 var label = this.el.select('label', true).first();
9224 var icon = this.el.select('i.fa-star', true).first();
9230 this.el.addClass(this.validClass);
9232 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9234 var feedback = this.el.select('.form-control-feedback', true).first();
9237 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9238 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9243 this.fireEvent('valid', this);
9247 * Mark this field as invalid
9248 * @param {String} msg The validation message
9250 markInvalid : function(msg)
9252 if(!this.el || this.preventMark){ // not rendered
9256 this.el.removeClass([this.invalidClass, this.validClass]);
9258 var feedback = this.el.select('.form-control-feedback', true).first();
9261 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9264 if(this.disabled || this.allowBlank){
9268 var label = this.el.select('label', true).first();
9269 var icon = this.el.select('i.fa-star', true).first();
9271 if(!this.getValue().length && label && !icon){
9272 this.el.createChild({
9274 cls : 'text-danger fa fa-lg fa-star',
9275 tooltip : 'This field is required',
9276 style : 'margin-right:5px;'
9280 this.el.addClass(this.invalidClass);
9282 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9284 var feedback = this.el.select('.form-control-feedback', true).first();
9287 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9289 if(this.getValue().length || this.forceFeedback){
9290 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9297 this.fireEvent('invalid', this, msg);
9305 * trigger field - base class for combo..
9310 * @class Roo.bootstrap.TriggerField
9311 * @extends Roo.bootstrap.Input
9312 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9313 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9314 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9315 * for which you can provide a custom implementation. For example:
9317 var trigger = new Roo.bootstrap.TriggerField();
9318 trigger.onTriggerClick = myTriggerFn;
9319 trigger.applyTo('my-field');
9322 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9323 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9324 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
9325 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9326 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9329 * Create a new TriggerField.
9330 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9331 * to the base TextField)
9333 Roo.bootstrap.TriggerField = function(config){
9334 this.mimicing = false;
9335 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9338 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
9340 * @cfg {String} triggerClass A CSS class to apply to the trigger
9343 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9348 * @cfg {Boolean} removable (true|false) special filter default false
9352 /** @cfg {Boolean} grow @hide */
9353 /** @cfg {Number} growMin @hide */
9354 /** @cfg {Number} growMax @hide */
9360 autoSize: Roo.emptyFn,
9367 actionMode : 'wrap',
9372 getAutoCreate : function(){
9374 var align = this.labelAlign || this.parentLabelAlign();
9379 cls: 'form-group' //input-group
9386 type : this.inputType,
9387 cls : 'form-control',
9388 autocomplete: 'new-password',
9389 placeholder : this.placeholder || ''
9393 input.name = this.name;
9396 input.cls += ' input-' + this.size;
9399 if (this.disabled) {
9400 input.disabled=true;
9403 var inputblock = input;
9405 if(this.hasFeedback && !this.allowBlank){
9409 cls: 'glyphicon form-control-feedback'
9412 if(this.removable && !this.editable && !this.tickable){
9414 cls : 'has-feedback',
9420 cls : 'roo-combo-removable-btn close'
9427 cls : 'has-feedback',
9436 if(this.removable && !this.editable && !this.tickable){
9438 cls : 'roo-removable',
9444 cls : 'roo-combo-removable-btn close'
9451 if (this.before || this.after) {
9454 cls : 'input-group',
9458 inputblock.cn.push({
9460 cls : 'input-group-addon',
9465 inputblock.cn.push(input);
9467 if(this.hasFeedback && !this.allowBlank){
9468 inputblock.cls += ' has-feedback';
9469 inputblock.cn.push(feedback);
9473 inputblock.cn.push({
9475 cls : 'input-group-addon',
9488 cls: 'form-hidden-field'
9502 cls: 'form-hidden-field'
9506 cls: 'roo-select2-choices',
9510 cls: 'roo-select2-search-field',
9523 cls: 'roo-select2-container input-group',
9528 // cls: 'typeahead typeahead-long dropdown-menu',
9529 // style: 'display:none'
9534 if(!this.multiple && this.showToggleBtn){
9540 if (this.caret != false) {
9543 cls: 'fa fa-' + this.caret
9550 cls : 'input-group-addon btn dropdown-toggle',
9555 cls: 'combobox-clear',
9569 combobox.cls += ' roo-select2-container-multi';
9572 if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
9574 // Roo.log("left and has label");
9578 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9579 tooltip : 'This field is required'
9584 cls : 'control-label col-sm-' + this.labelWidth,
9585 html : this.fieldLabel
9589 cls : "col-sm-" + (12 - this.labelWidth),
9597 if(this.indicatorpos == 'right'){
9602 cls : 'control-label col-sm-' + this.labelWidth,
9603 html : this.fieldLabel
9608 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9609 tooltip : 'This field is required'
9612 cls : "col-sm-" + (12 - this.labelWidth),
9621 } else if ( this.fieldLabel.length) {
9622 // Roo.log(" label");
9626 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9627 tooltip : 'This field is required'
9631 //cls : 'input-group-addon',
9632 html : this.fieldLabel
9640 if(this.indicatorpos == 'right'){
9645 //cls : 'input-group-addon',
9646 html : this.fieldLabel
9651 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9652 tooltip : 'This field is required'
9663 // Roo.log(" no label && no align");
9670 ['xs','sm','md','lg'].map(function(size){
9671 if (settings[size]) {
9672 cfg.cls += ' col-' + size + '-' + settings[size];
9683 onResize : function(w, h){
9684 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9685 // if(typeof w == 'number'){
9686 // var x = w - this.trigger.getWidth();
9687 // this.inputEl().setWidth(this.adjustWidth('input', x));
9688 // this.trigger.setStyle('left', x+'px');
9693 adjustSize : Roo.BoxComponent.prototype.adjustSize,
9696 getResizeEl : function(){
9697 return this.inputEl();
9701 getPositionEl : function(){
9702 return this.inputEl();
9706 alignErrorIcon : function(){
9707 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9711 initEvents : function(){
9715 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9716 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9717 if(!this.multiple && this.showToggleBtn){
9718 this.trigger = this.el.select('span.dropdown-toggle',true).first();
9719 if(this.hideTrigger){
9720 this.trigger.setDisplayed(false);
9722 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9726 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9729 if(this.removable && !this.editable && !this.tickable){
9730 var close = this.closeTriggerEl();
9733 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9734 close.on('click', this.removeBtnClick, this, close);
9738 //this.trigger.addClassOnOver('x-form-trigger-over');
9739 //this.trigger.addClassOnClick('x-form-trigger-click');
9742 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9746 closeTriggerEl : function()
9748 var close = this.el.select('.roo-combo-removable-btn', true).first();
9749 return close ? close : false;
9752 removeBtnClick : function(e, h, el)
9756 if(this.fireEvent("remove", this) !== false){
9758 this.fireEvent("afterremove", this)
9762 createList : function()
9764 this.list = Roo.get(document.body).createChild({
9766 cls: 'typeahead typeahead-long dropdown-menu',
9767 style: 'display:none'
9770 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9775 initTrigger : function(){
9780 onDestroy : function(){
9782 this.trigger.removeAllListeners();
9783 // this.trigger.remove();
9786 // this.wrap.remove();
9788 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9792 onFocus : function(){
9793 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9796 this.wrap.addClass('x-trigger-wrap-focus');
9797 this.mimicing = true;
9798 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9799 if(this.monitorTab){
9800 this.el.on("keydown", this.checkTab, this);
9807 checkTab : function(e){
9808 if(e.getKey() == e.TAB){
9814 onBlur : function(){
9819 mimicBlur : function(e, t){
9821 if(!this.wrap.contains(t) && this.validateBlur()){
9828 triggerBlur : function(){
9829 this.mimicing = false;
9830 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9831 if(this.monitorTab){
9832 this.el.un("keydown", this.checkTab, this);
9834 //this.wrap.removeClass('x-trigger-wrap-focus');
9835 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9839 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9840 validateBlur : function(e, t){
9845 onDisable : function(){
9846 this.inputEl().dom.disabled = true;
9847 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9849 // this.wrap.addClass('x-item-disabled');
9854 onEnable : function(){
9855 this.inputEl().dom.disabled = false;
9856 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9858 // this.el.removeClass('x-item-disabled');
9863 onShow : function(){
9864 var ae = this.getActionEl();
9867 ae.dom.style.display = '';
9868 ae.dom.style.visibility = 'visible';
9874 onHide : function(){
9875 var ae = this.getActionEl();
9876 ae.dom.style.display = 'none';
9880 * The function that should handle the trigger's click event. This method does nothing by default until overridden
9881 * by an implementing function.
9883 * @param {EventObject} e
9885 onTriggerClick : Roo.emptyFn
9889 * Ext JS Library 1.1.1
9890 * Copyright(c) 2006-2007, Ext JS, LLC.
9892 * Originally Released Under LGPL - original licence link has changed is not relivant.
9895 * <script type="text/javascript">
9900 * @class Roo.data.SortTypes
9902 * Defines the default sorting (casting?) comparison functions used when sorting data.
9904 Roo.data.SortTypes = {
9906 * Default sort that does nothing
9907 * @param {Mixed} s The value being converted
9908 * @return {Mixed} The comparison value
9915 * The regular expression used to strip tags
9919 stripTagsRE : /<\/?[^>]+>/gi,
9922 * Strips all HTML tags to sort on text only
9923 * @param {Mixed} s The value being converted
9924 * @return {String} The comparison value
9926 asText : function(s){
9927 return String(s).replace(this.stripTagsRE, "");
9931 * Strips all HTML tags to sort on text only - Case insensitive
9932 * @param {Mixed} s The value being converted
9933 * @return {String} The comparison value
9935 asUCText : function(s){
9936 return String(s).toUpperCase().replace(this.stripTagsRE, "");
9940 * Case insensitive string
9941 * @param {Mixed} s The value being converted
9942 * @return {String} The comparison value
9944 asUCString : function(s) {
9945 return String(s).toUpperCase();
9950 * @param {Mixed} s The value being converted
9951 * @return {Number} The comparison value
9953 asDate : function(s) {
9957 if(s instanceof Date){
9960 return Date.parse(String(s));
9965 * @param {Mixed} s The value being converted
9966 * @return {Float} The comparison value
9968 asFloat : function(s) {
9969 var val = parseFloat(String(s).replace(/,/g, ""));
9978 * @param {Mixed} s The value being converted
9979 * @return {Number} The comparison value
9981 asInt : function(s) {
9982 var val = parseInt(String(s).replace(/,/g, ""));
9990 * Ext JS Library 1.1.1
9991 * Copyright(c) 2006-2007, Ext JS, LLC.
9993 * Originally Released Under LGPL - original licence link has changed is not relivant.
9996 * <script type="text/javascript">
10000 * @class Roo.data.Record
10001 * Instances of this class encapsulate both record <em>definition</em> information, and record
10002 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10003 * to access Records cached in an {@link Roo.data.Store} object.<br>
10005 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10006 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10009 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10011 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10012 * {@link #create}. The parameters are the same.
10013 * @param {Array} data An associative Array of data values keyed by the field name.
10014 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10015 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10016 * not specified an integer id is generated.
10018 Roo.data.Record = function(data, id){
10019 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10024 * Generate a constructor for a specific record layout.
10025 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10026 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10027 * Each field definition object may contain the following properties: <ul>
10028 * <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,
10029 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10030 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10031 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10032 * is being used, then this is a string containing the javascript expression to reference the data relative to
10033 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10034 * to the data item relative to the record element. If the mapping expression is the same as the field name,
10035 * this may be omitted.</p></li>
10036 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10037 * <ul><li>auto (Default, implies no conversion)</li>
10042 * <li>date</li></ul></p></li>
10043 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10044 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10045 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10046 * by the Reader into an object that will be stored in the Record. It is passed the
10047 * following parameters:<ul>
10048 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10050 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10052 * <br>usage:<br><pre><code>
10053 var TopicRecord = Roo.data.Record.create(
10054 {name: 'title', mapping: 'topic_title'},
10055 {name: 'author', mapping: 'username'},
10056 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10057 {name: 'lastPost', mapping: 'post_time', type: 'date'},
10058 {name: 'lastPoster', mapping: 'user2'},
10059 {name: 'excerpt', mapping: 'post_text'}
10062 var myNewRecord = new TopicRecord({
10063 title: 'Do my job please',
10066 lastPost: new Date(),
10067 lastPoster: 'Animal',
10068 excerpt: 'No way dude!'
10070 myStore.add(myNewRecord);
10075 Roo.data.Record.create = function(o){
10076 var f = function(){
10077 f.superclass.constructor.apply(this, arguments);
10079 Roo.extend(f, Roo.data.Record);
10080 var p = f.prototype;
10081 p.fields = new Roo.util.MixedCollection(false, function(field){
10084 for(var i = 0, len = o.length; i < len; i++){
10085 p.fields.add(new Roo.data.Field(o[i]));
10087 f.getField = function(name){
10088 return p.fields.get(name);
10093 Roo.data.Record.AUTO_ID = 1000;
10094 Roo.data.Record.EDIT = 'edit';
10095 Roo.data.Record.REJECT = 'reject';
10096 Roo.data.Record.COMMIT = 'commit';
10098 Roo.data.Record.prototype = {
10100 * Readonly flag - true if this record has been modified.
10109 join : function(store){
10110 this.store = store;
10114 * Set the named field to the specified value.
10115 * @param {String} name The name of the field to set.
10116 * @param {Object} value The value to set the field to.
10118 set : function(name, value){
10119 if(this.data[name] == value){
10123 if(!this.modified){
10124 this.modified = {};
10126 if(typeof this.modified[name] == 'undefined'){
10127 this.modified[name] = this.data[name];
10129 this.data[name] = value;
10130 if(!this.editing && this.store){
10131 this.store.afterEdit(this);
10136 * Get the value of the named field.
10137 * @param {String} name The name of the field to get the value of.
10138 * @return {Object} The value of the field.
10140 get : function(name){
10141 return this.data[name];
10145 beginEdit : function(){
10146 this.editing = true;
10147 this.modified = {};
10151 cancelEdit : function(){
10152 this.editing = false;
10153 delete this.modified;
10157 endEdit : function(){
10158 this.editing = false;
10159 if(this.dirty && this.store){
10160 this.store.afterEdit(this);
10165 * Usually called by the {@link Roo.data.Store} which owns the Record.
10166 * Rejects all changes made to the Record since either creation, or the last commit operation.
10167 * Modified fields are reverted to their original values.
10169 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10170 * of reject operations.
10172 reject : function(){
10173 var m = this.modified;
10175 if(typeof m[n] != "function"){
10176 this.data[n] = m[n];
10179 this.dirty = false;
10180 delete this.modified;
10181 this.editing = false;
10183 this.store.afterReject(this);
10188 * Usually called by the {@link Roo.data.Store} which owns the Record.
10189 * Commits all changes made to the Record since either creation, or the last commit operation.
10191 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10192 * of commit operations.
10194 commit : function(){
10195 this.dirty = false;
10196 delete this.modified;
10197 this.editing = false;
10199 this.store.afterCommit(this);
10204 hasError : function(){
10205 return this.error != null;
10209 clearError : function(){
10214 * Creates a copy of this record.
10215 * @param {String} id (optional) A new record id if you don't want to use this record's id
10218 copy : function(newId) {
10219 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10223 * Ext JS Library 1.1.1
10224 * Copyright(c) 2006-2007, Ext JS, LLC.
10226 * Originally Released Under LGPL - original licence link has changed is not relivant.
10229 * <script type="text/javascript">
10235 * @class Roo.data.Store
10236 * @extends Roo.util.Observable
10237 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10238 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10240 * 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
10241 * has no knowledge of the format of the data returned by the Proxy.<br>
10243 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10244 * instances from the data object. These records are cached and made available through accessor functions.
10246 * Creates a new Store.
10247 * @param {Object} config A config object containing the objects needed for the Store to access data,
10248 * and read the data into Records.
10250 Roo.data.Store = function(config){
10251 this.data = new Roo.util.MixedCollection(false);
10252 this.data.getKey = function(o){
10255 this.baseParams = {};
10257 this.paramNames = {
10262 "multisort" : "_multisort"
10265 if(config && config.data){
10266 this.inlineData = config.data;
10267 delete config.data;
10270 Roo.apply(this, config);
10272 if(this.reader){ // reader passed
10273 this.reader = Roo.factory(this.reader, Roo.data);
10274 this.reader.xmodule = this.xmodule || false;
10275 if(!this.recordType){
10276 this.recordType = this.reader.recordType;
10278 if(this.reader.onMetaChange){
10279 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10283 if(this.recordType){
10284 this.fields = this.recordType.prototype.fields;
10286 this.modified = [];
10290 * @event datachanged
10291 * Fires when the data cache has changed, and a widget which is using this Store
10292 * as a Record cache should refresh its view.
10293 * @param {Store} this
10295 datachanged : true,
10297 * @event metachange
10298 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10299 * @param {Store} this
10300 * @param {Object} meta The JSON metadata
10305 * Fires when Records have been added to the Store
10306 * @param {Store} this
10307 * @param {Roo.data.Record[]} records The array of Records added
10308 * @param {Number} index The index at which the record(s) were added
10313 * Fires when a Record has been removed from the Store
10314 * @param {Store} this
10315 * @param {Roo.data.Record} record The Record that was removed
10316 * @param {Number} index The index at which the record was removed
10321 * Fires when a Record has been updated
10322 * @param {Store} this
10323 * @param {Roo.data.Record} record The Record that was updated
10324 * @param {String} operation The update operation being performed. Value may be one of:
10326 Roo.data.Record.EDIT
10327 Roo.data.Record.REJECT
10328 Roo.data.Record.COMMIT
10334 * Fires when the data cache has been cleared.
10335 * @param {Store} this
10339 * @event beforeload
10340 * Fires before a request is made for a new data object. If the beforeload handler returns false
10341 * the load action will be canceled.
10342 * @param {Store} this
10343 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10347 * @event beforeloadadd
10348 * Fires after a new set of Records has been loaded.
10349 * @param {Store} this
10350 * @param {Roo.data.Record[]} records The Records that were loaded
10351 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10353 beforeloadadd : true,
10356 * Fires after a new set of Records has been loaded, before they are added to the store.
10357 * @param {Store} this
10358 * @param {Roo.data.Record[]} records The Records that were loaded
10359 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10360 * @params {Object} return from reader
10364 * @event loadexception
10365 * Fires if an exception occurs in the Proxy during loading.
10366 * Called with the signature of the Proxy's "loadexception" event.
10367 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10370 * @param {Object} return from JsonData.reader() - success, totalRecords, records
10371 * @param {Object} load options
10372 * @param {Object} jsonData from your request (normally this contains the Exception)
10374 loadexception : true
10378 this.proxy = Roo.factory(this.proxy, Roo.data);
10379 this.proxy.xmodule = this.xmodule || false;
10380 this.relayEvents(this.proxy, ["loadexception"]);
10382 this.sortToggle = {};
10383 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10385 Roo.data.Store.superclass.constructor.call(this);
10387 if(this.inlineData){
10388 this.loadData(this.inlineData);
10389 delete this.inlineData;
10393 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10395 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
10396 * without a remote query - used by combo/forms at present.
10400 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10403 * @cfg {Array} data Inline data to be loaded when the store is initialized.
10406 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10407 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10410 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10411 * on any HTTP request
10414 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10417 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10421 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10422 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10424 remoteSort : false,
10427 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10428 * loaded or when a record is removed. (defaults to false).
10430 pruneModifiedRecords : false,
10433 lastOptions : null,
10436 * Add Records to the Store and fires the add event.
10437 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10439 add : function(records){
10440 records = [].concat(records);
10441 for(var i = 0, len = records.length; i < len; i++){
10442 records[i].join(this);
10444 var index = this.data.length;
10445 this.data.addAll(records);
10446 this.fireEvent("add", this, records, index);
10450 * Remove a Record from the Store and fires the remove event.
10451 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10453 remove : function(record){
10454 var index = this.data.indexOf(record);
10455 this.data.removeAt(index);
10456 if(this.pruneModifiedRecords){
10457 this.modified.remove(record);
10459 this.fireEvent("remove", this, record, index);
10463 * Remove all Records from the Store and fires the clear event.
10465 removeAll : function(){
10467 if(this.pruneModifiedRecords){
10468 this.modified = [];
10470 this.fireEvent("clear", this);
10474 * Inserts Records to the Store at the given index and fires the add event.
10475 * @param {Number} index The start index at which to insert the passed Records.
10476 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10478 insert : function(index, records){
10479 records = [].concat(records);
10480 for(var i = 0, len = records.length; i < len; i++){
10481 this.data.insert(index, records[i]);
10482 records[i].join(this);
10484 this.fireEvent("add", this, records, index);
10488 * Get the index within the cache of the passed Record.
10489 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10490 * @return {Number} The index of the passed Record. Returns -1 if not found.
10492 indexOf : function(record){
10493 return this.data.indexOf(record);
10497 * Get the index within the cache of the Record with the passed id.
10498 * @param {String} id The id of the Record to find.
10499 * @return {Number} The index of the Record. Returns -1 if not found.
10501 indexOfId : function(id){
10502 return this.data.indexOfKey(id);
10506 * Get the Record with the specified id.
10507 * @param {String} id The id of the Record to find.
10508 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10510 getById : function(id){
10511 return this.data.key(id);
10515 * Get the Record at the specified index.
10516 * @param {Number} index The index of the Record to find.
10517 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10519 getAt : function(index){
10520 return this.data.itemAt(index);
10524 * Returns a range of Records between specified indices.
10525 * @param {Number} startIndex (optional) The starting index (defaults to 0)
10526 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10527 * @return {Roo.data.Record[]} An array of Records
10529 getRange : function(start, end){
10530 return this.data.getRange(start, end);
10534 storeOptions : function(o){
10535 o = Roo.apply({}, o);
10538 this.lastOptions = o;
10542 * Loads the Record cache from the configured Proxy using the configured Reader.
10544 * If using remote paging, then the first load call must specify the <em>start</em>
10545 * and <em>limit</em> properties in the options.params property to establish the initial
10546 * position within the dataset, and the number of Records to cache on each read from the Proxy.
10548 * <strong>It is important to note that for remote data sources, loading is asynchronous,
10549 * and this call will return before the new data has been loaded. Perform any post-processing
10550 * in a callback function, or in a "load" event handler.</strong>
10552 * @param {Object} options An object containing properties which control loading options:<ul>
10553 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10554 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10555 * passed the following arguments:<ul>
10556 * <li>r : Roo.data.Record[]</li>
10557 * <li>options: Options object from the load call</li>
10558 * <li>success: Boolean success indicator</li></ul></li>
10559 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10560 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10563 load : function(options){
10564 options = options || {};
10565 if(this.fireEvent("beforeload", this, options) !== false){
10566 this.storeOptions(options);
10567 var p = Roo.apply(options.params || {}, this.baseParams);
10568 // if meta was not loaded from remote source.. try requesting it.
10569 if (!this.reader.metaFromRemote) {
10570 p._requestMeta = 1;
10572 if(this.sortInfo && this.remoteSort){
10573 var pn = this.paramNames;
10574 p[pn["sort"]] = this.sortInfo.field;
10575 p[pn["dir"]] = this.sortInfo.direction;
10577 if (this.multiSort) {
10578 var pn = this.paramNames;
10579 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10582 this.proxy.load(p, this.reader, this.loadRecords, this, options);
10587 * Reloads the Record cache from the configured Proxy using the configured Reader and
10588 * the options from the last load operation performed.
10589 * @param {Object} options (optional) An object containing properties which may override the options
10590 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10591 * the most recently used options are reused).
10593 reload : function(options){
10594 this.load(Roo.applyIf(options||{}, this.lastOptions));
10598 // Called as a callback by the Reader during a load operation.
10599 loadRecords : function(o, options, success){
10600 if(!o || success === false){
10601 if(success !== false){
10602 this.fireEvent("load", this, [], options, o);
10604 if(options.callback){
10605 options.callback.call(options.scope || this, [], options, false);
10609 // if data returned failure - throw an exception.
10610 if (o.success === false) {
10611 // show a message if no listener is registered.
10612 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10613 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10615 // loadmask wil be hooked into this..
10616 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10619 var r = o.records, t = o.totalRecords || r.length;
10621 this.fireEvent("beforeloadadd", this, r, options, o);
10623 if(!options || options.add !== true){
10624 if(this.pruneModifiedRecords){
10625 this.modified = [];
10627 for(var i = 0, len = r.length; i < len; i++){
10631 this.data = this.snapshot;
10632 delete this.snapshot;
10635 this.data.addAll(r);
10636 this.totalLength = t;
10638 this.fireEvent("datachanged", this);
10640 this.totalLength = Math.max(t, this.data.length+r.length);
10643 this.fireEvent("load", this, r, options, o);
10644 if(options.callback){
10645 options.callback.call(options.scope || this, r, options, true);
10651 * Loads data from a passed data block. A Reader which understands the format of the data
10652 * must have been configured in the constructor.
10653 * @param {Object} data The data block from which to read the Records. The format of the data expected
10654 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10655 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10657 loadData : function(o, append){
10658 var r = this.reader.readRecords(o);
10659 this.loadRecords(r, {add: append}, true);
10663 * Gets the number of cached records.
10665 * <em>If using paging, this may not be the total size of the dataset. If the data object
10666 * used by the Reader contains the dataset size, then the getTotalCount() function returns
10667 * the data set size</em>
10669 getCount : function(){
10670 return this.data.length || 0;
10674 * Gets the total number of records in the dataset as returned by the server.
10676 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10677 * the dataset size</em>
10679 getTotalCount : function(){
10680 return this.totalLength || 0;
10684 * Returns the sort state of the Store as an object with two properties:
10686 field {String} The name of the field by which the Records are sorted
10687 direction {String} The sort order, "ASC" or "DESC"
10690 getSortState : function(){
10691 return this.sortInfo;
10695 applySort : function(){
10696 if(this.sortInfo && !this.remoteSort){
10697 var s = this.sortInfo, f = s.field;
10698 var st = this.fields.get(f).sortType;
10699 var fn = function(r1, r2){
10700 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10701 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10703 this.data.sort(s.direction, fn);
10704 if(this.snapshot && this.snapshot != this.data){
10705 this.snapshot.sort(s.direction, fn);
10711 * Sets the default sort column and order to be used by the next load operation.
10712 * @param {String} fieldName The name of the field to sort by.
10713 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10715 setDefaultSort : function(field, dir){
10716 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10720 * Sort the Records.
10721 * If remote sorting is used, the sort is performed on the server, and the cache is
10722 * reloaded. If local sorting is used, the cache is sorted internally.
10723 * @param {String} fieldName The name of the field to sort by.
10724 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10726 sort : function(fieldName, dir){
10727 var f = this.fields.get(fieldName);
10729 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10731 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10732 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10737 this.sortToggle[f.name] = dir;
10738 this.sortInfo = {field: f.name, direction: dir};
10739 if(!this.remoteSort){
10741 this.fireEvent("datachanged", this);
10743 this.load(this.lastOptions);
10748 * Calls the specified function for each of the Records in the cache.
10749 * @param {Function} fn The function to call. The Record is passed as the first parameter.
10750 * Returning <em>false</em> aborts and exits the iteration.
10751 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10753 each : function(fn, scope){
10754 this.data.each(fn, scope);
10758 * Gets all records modified since the last commit. Modified records are persisted across load operations
10759 * (e.g., during paging).
10760 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10762 getModifiedRecords : function(){
10763 return this.modified;
10767 createFilterFn : function(property, value, anyMatch){
10768 if(!value.exec){ // not a regex
10769 value = String(value);
10770 if(value.length == 0){
10773 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10775 return function(r){
10776 return value.test(r.data[property]);
10781 * Sums the value of <i>property</i> for each record between start and end and returns the result.
10782 * @param {String} property A field on your records
10783 * @param {Number} start The record index to start at (defaults to 0)
10784 * @param {Number} end The last record index to include (defaults to length - 1)
10785 * @return {Number} The sum
10787 sum : function(property, start, end){
10788 var rs = this.data.items, v = 0;
10789 start = start || 0;
10790 end = (end || end === 0) ? end : rs.length-1;
10792 for(var i = start; i <= end; i++){
10793 v += (rs[i].data[property] || 0);
10799 * Filter the records by a specified property.
10800 * @param {String} field A field on your records
10801 * @param {String/RegExp} value Either a string that the field
10802 * should start with or a RegExp to test against the field
10803 * @param {Boolean} anyMatch True to match any part not just the beginning
10805 filter : function(property, value, anyMatch){
10806 var fn = this.createFilterFn(property, value, anyMatch);
10807 return fn ? this.filterBy(fn) : this.clearFilter();
10811 * Filter by a function. The specified function will be called with each
10812 * record in this data source. If the function returns true the record is included,
10813 * otherwise it is filtered.
10814 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10815 * @param {Object} scope (optional) The scope of the function (defaults to this)
10817 filterBy : function(fn, scope){
10818 this.snapshot = this.snapshot || this.data;
10819 this.data = this.queryBy(fn, scope||this);
10820 this.fireEvent("datachanged", this);
10824 * Query the records by a specified property.
10825 * @param {String} field A field on your records
10826 * @param {String/RegExp} value Either a string that the field
10827 * should start with or a RegExp to test against the field
10828 * @param {Boolean} anyMatch True to match any part not just the beginning
10829 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10831 query : function(property, value, anyMatch){
10832 var fn = this.createFilterFn(property, value, anyMatch);
10833 return fn ? this.queryBy(fn) : this.data.clone();
10837 * Query by a function. The specified function will be called with each
10838 * record in this data source. If the function returns true the record is included
10840 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10841 * @param {Object} scope (optional) The scope of the function (defaults to this)
10842 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10844 queryBy : function(fn, scope){
10845 var data = this.snapshot || this.data;
10846 return data.filterBy(fn, scope||this);
10850 * Collects unique values for a particular dataIndex from this store.
10851 * @param {String} dataIndex The property to collect
10852 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10853 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10854 * @return {Array} An array of the unique values
10856 collect : function(dataIndex, allowNull, bypassFilter){
10857 var d = (bypassFilter === true && this.snapshot) ?
10858 this.snapshot.items : this.data.items;
10859 var v, sv, r = [], l = {};
10860 for(var i = 0, len = d.length; i < len; i++){
10861 v = d[i].data[dataIndex];
10863 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10872 * Revert to a view of the Record cache with no filtering applied.
10873 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10875 clearFilter : function(suppressEvent){
10876 if(this.snapshot && this.snapshot != this.data){
10877 this.data = this.snapshot;
10878 delete this.snapshot;
10879 if(suppressEvent !== true){
10880 this.fireEvent("datachanged", this);
10886 afterEdit : function(record){
10887 if(this.modified.indexOf(record) == -1){
10888 this.modified.push(record);
10890 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10894 afterReject : function(record){
10895 this.modified.remove(record);
10896 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10900 afterCommit : function(record){
10901 this.modified.remove(record);
10902 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10906 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10907 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10909 commitChanges : function(){
10910 var m = this.modified.slice(0);
10911 this.modified = [];
10912 for(var i = 0, len = m.length; i < len; i++){
10918 * Cancel outstanding changes on all changed records.
10920 rejectChanges : function(){
10921 var m = this.modified.slice(0);
10922 this.modified = [];
10923 for(var i = 0, len = m.length; i < len; i++){
10928 onMetaChange : function(meta, rtype, o){
10929 this.recordType = rtype;
10930 this.fields = rtype.prototype.fields;
10931 delete this.snapshot;
10932 this.sortInfo = meta.sortInfo || this.sortInfo;
10933 this.modified = [];
10934 this.fireEvent('metachange', this, this.reader.meta);
10937 moveIndex : function(data, type)
10939 var index = this.indexOf(data);
10941 var newIndex = index + type;
10945 this.insert(newIndex, data);
10950 * Ext JS Library 1.1.1
10951 * Copyright(c) 2006-2007, Ext JS, LLC.
10953 * Originally Released Under LGPL - original licence link has changed is not relivant.
10956 * <script type="text/javascript">
10960 * @class Roo.data.SimpleStore
10961 * @extends Roo.data.Store
10962 * Small helper class to make creating Stores from Array data easier.
10963 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10964 * @cfg {Array} fields An array of field definition objects, or field name strings.
10965 * @cfg {Array} data The multi-dimensional array of data
10967 * @param {Object} config
10969 Roo.data.SimpleStore = function(config){
10970 Roo.data.SimpleStore.superclass.constructor.call(this, {
10972 reader: new Roo.data.ArrayReader({
10975 Roo.data.Record.create(config.fields)
10977 proxy : new Roo.data.MemoryProxy(config.data)
10981 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10983 * Ext JS Library 1.1.1
10984 * Copyright(c) 2006-2007, Ext JS, LLC.
10986 * Originally Released Under LGPL - original licence link has changed is not relivant.
10989 * <script type="text/javascript">
10994 * @extends Roo.data.Store
10995 * @class Roo.data.JsonStore
10996 * Small helper class to make creating Stores for JSON data easier. <br/>
10998 var store = new Roo.data.JsonStore({
10999 url: 'get-images.php',
11001 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11004 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11005 * JsonReader and HttpProxy (unless inline data is provided).</b>
11006 * @cfg {Array} fields An array of field definition objects, or field name strings.
11008 * @param {Object} config
11010 Roo.data.JsonStore = function(c){
11011 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11012 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11013 reader: new Roo.data.JsonReader(c, c.fields)
11016 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11018 * Ext JS Library 1.1.1
11019 * Copyright(c) 2006-2007, Ext JS, LLC.
11021 * Originally Released Under LGPL - original licence link has changed is not relivant.
11024 * <script type="text/javascript">
11028 Roo.data.Field = function(config){
11029 if(typeof config == "string"){
11030 config = {name: config};
11032 Roo.apply(this, config);
11035 this.type = "auto";
11038 var st = Roo.data.SortTypes;
11039 // named sortTypes are supported, here we look them up
11040 if(typeof this.sortType == "string"){
11041 this.sortType = st[this.sortType];
11044 // set default sortType for strings and dates
11045 if(!this.sortType){
11048 this.sortType = st.asUCString;
11051 this.sortType = st.asDate;
11054 this.sortType = st.none;
11059 var stripRe = /[\$,%]/g;
11061 // prebuilt conversion function for this field, instead of
11062 // switching every time we're reading a value
11064 var cv, dateFormat = this.dateFormat;
11069 cv = function(v){ return v; };
11072 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11076 return v !== undefined && v !== null && v !== '' ?
11077 parseInt(String(v).replace(stripRe, ""), 10) : '';
11082 return v !== undefined && v !== null && v !== '' ?
11083 parseFloat(String(v).replace(stripRe, ""), 10) : '';
11088 cv = function(v){ return v === true || v === "true" || v == 1; };
11095 if(v instanceof Date){
11099 if(dateFormat == "timestamp"){
11100 return new Date(v*1000);
11102 return Date.parseDate(v, dateFormat);
11104 var parsed = Date.parse(v);
11105 return parsed ? new Date(parsed) : null;
11114 Roo.data.Field.prototype = {
11122 * Ext JS Library 1.1.1
11123 * Copyright(c) 2006-2007, Ext JS, LLC.
11125 * Originally Released Under LGPL - original licence link has changed is not relivant.
11128 * <script type="text/javascript">
11131 // Base class for reading structured data from a data source. This class is intended to be
11132 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11135 * @class Roo.data.DataReader
11136 * Base class for reading structured data from a data source. This class is intended to be
11137 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11140 Roo.data.DataReader = function(meta, recordType){
11144 this.recordType = recordType instanceof Array ?
11145 Roo.data.Record.create(recordType) : recordType;
11148 Roo.data.DataReader.prototype = {
11150 * Create an empty record
11151 * @param {Object} data (optional) - overlay some values
11152 * @return {Roo.data.Record} record created.
11154 newRow : function(d) {
11156 this.recordType.prototype.fields.each(function(c) {
11158 case 'int' : da[c.name] = 0; break;
11159 case 'date' : da[c.name] = new Date(); break;
11160 case 'float' : da[c.name] = 0.0; break;
11161 case 'boolean' : da[c.name] = false; break;
11162 default : da[c.name] = ""; break;
11166 return new this.recordType(Roo.apply(da, d));
11171 * Ext JS Library 1.1.1
11172 * Copyright(c) 2006-2007, Ext JS, LLC.
11174 * Originally Released Under LGPL - original licence link has changed is not relivant.
11177 * <script type="text/javascript">
11181 * @class Roo.data.DataProxy
11182 * @extends Roo.data.Observable
11183 * This class is an abstract base class for implementations which provide retrieval of
11184 * unformatted data objects.<br>
11186 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11187 * (of the appropriate type which knows how to parse the data object) to provide a block of
11188 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11190 * Custom implementations must implement the load method as described in
11191 * {@link Roo.data.HttpProxy#load}.
11193 Roo.data.DataProxy = function(){
11196 * @event beforeload
11197 * Fires before a network request is made to retrieve a data object.
11198 * @param {Object} This DataProxy object.
11199 * @param {Object} params The params parameter to the load function.
11204 * Fires before the load method's callback is called.
11205 * @param {Object} This DataProxy object.
11206 * @param {Object} o The data object.
11207 * @param {Object} arg The callback argument object passed to the load function.
11211 * @event loadexception
11212 * Fires if an Exception occurs during data retrieval.
11213 * @param {Object} This DataProxy object.
11214 * @param {Object} o The data object.
11215 * @param {Object} arg The callback argument object passed to the load function.
11216 * @param {Object} e The Exception.
11218 loadexception : true
11220 Roo.data.DataProxy.superclass.constructor.call(this);
11223 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11226 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11230 * Ext JS Library 1.1.1
11231 * Copyright(c) 2006-2007, Ext JS, LLC.
11233 * Originally Released Under LGPL - original licence link has changed is not relivant.
11236 * <script type="text/javascript">
11239 * @class Roo.data.MemoryProxy
11240 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11241 * to the Reader when its load method is called.
11243 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11245 Roo.data.MemoryProxy = function(data){
11249 Roo.data.MemoryProxy.superclass.constructor.call(this);
11253 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11256 * Load data from the requested source (in this case an in-memory
11257 * data object passed to the constructor), read the data object into
11258 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11259 * process that block using the passed callback.
11260 * @param {Object} params This parameter is not used by the MemoryProxy class.
11261 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11262 * object into a block of Roo.data.Records.
11263 * @param {Function} callback The function into which to pass the block of Roo.data.records.
11264 * The function must be passed <ul>
11265 * <li>The Record block object</li>
11266 * <li>The "arg" argument from the load function</li>
11267 * <li>A boolean success indicator</li>
11269 * @param {Object} scope The scope in which to call the callback
11270 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11272 load : function(params, reader, callback, scope, arg){
11273 params = params || {};
11276 result = reader.readRecords(this.data);
11278 this.fireEvent("loadexception", this, arg, null, e);
11279 callback.call(scope, null, arg, false);
11282 callback.call(scope, result, arg, true);
11286 update : function(params, records){
11291 * Ext JS Library 1.1.1
11292 * Copyright(c) 2006-2007, Ext JS, LLC.
11294 * Originally Released Under LGPL - original licence link has changed is not relivant.
11297 * <script type="text/javascript">
11300 * @class Roo.data.HttpProxy
11301 * @extends Roo.data.DataProxy
11302 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11303 * configured to reference a certain URL.<br><br>
11305 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11306 * from which the running page was served.<br><br>
11308 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11310 * Be aware that to enable the browser to parse an XML document, the server must set
11311 * the Content-Type header in the HTTP response to "text/xml".
11313 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11314 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
11315 * will be used to make the request.
11317 Roo.data.HttpProxy = function(conn){
11318 Roo.data.HttpProxy.superclass.constructor.call(this);
11319 // is conn a conn config or a real conn?
11321 this.useAjax = !conn || !conn.events;
11325 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11326 // thse are take from connection...
11329 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11332 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11333 * extra parameters to each request made by this object. (defaults to undefined)
11336 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11337 * to each request made by this object. (defaults to undefined)
11340 * @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)
11343 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11346 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11352 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11356 * Return the {@link Roo.data.Connection} object being used by this Proxy.
11357 * @return {Connection} The Connection object. This object may be used to subscribe to events on
11358 * a finer-grained basis than the DataProxy events.
11360 getConnection : function(){
11361 return this.useAjax ? Roo.Ajax : this.conn;
11365 * Load data from the configured {@link Roo.data.Connection}, read the data object into
11366 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11367 * process that block using the passed callback.
11368 * @param {Object} params An object containing properties which are to be used as HTTP parameters
11369 * for the request to the remote server.
11370 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11371 * object into a block of Roo.data.Records.
11372 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11373 * The function must be passed <ul>
11374 * <li>The Record block object</li>
11375 * <li>The "arg" argument from the load function</li>
11376 * <li>A boolean success indicator</li>
11378 * @param {Object} scope The scope in which to call the callback
11379 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11381 load : function(params, reader, callback, scope, arg){
11382 if(this.fireEvent("beforeload", this, params) !== false){
11384 params : params || {},
11386 callback : callback,
11391 callback : this.loadResponse,
11395 Roo.applyIf(o, this.conn);
11396 if(this.activeRequest){
11397 Roo.Ajax.abort(this.activeRequest);
11399 this.activeRequest = Roo.Ajax.request(o);
11401 this.conn.request(o);
11404 callback.call(scope||this, null, arg, false);
11409 loadResponse : function(o, success, response){
11410 delete this.activeRequest;
11412 this.fireEvent("loadexception", this, o, response);
11413 o.request.callback.call(o.request.scope, null, o.request.arg, false);
11418 result = o.reader.read(response);
11420 this.fireEvent("loadexception", this, o, response, e);
11421 o.request.callback.call(o.request.scope, null, o.request.arg, false);
11425 this.fireEvent("load", this, o, o.request.arg);
11426 o.request.callback.call(o.request.scope, result, o.request.arg, true);
11430 update : function(dataSet){
11435 updateResponse : function(dataSet){
11440 * Ext JS Library 1.1.1
11441 * Copyright(c) 2006-2007, Ext JS, LLC.
11443 * Originally Released Under LGPL - original licence link has changed is not relivant.
11446 * <script type="text/javascript">
11450 * @class Roo.data.ScriptTagProxy
11451 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11452 * other than the originating domain of the running page.<br><br>
11454 * <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
11455 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11457 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11458 * source code that is used as the source inside a <script> tag.<br><br>
11460 * In order for the browser to process the returned data, the server must wrap the data object
11461 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11462 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11463 * depending on whether the callback name was passed:
11466 boolean scriptTag = false;
11467 String cb = request.getParameter("callback");
11470 response.setContentType("text/javascript");
11472 response.setContentType("application/x-json");
11474 Writer out = response.getWriter();
11476 out.write(cb + "(");
11478 out.print(dataBlock.toJsonString());
11485 * @param {Object} config A configuration object.
11487 Roo.data.ScriptTagProxy = function(config){
11488 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11489 Roo.apply(this, config);
11490 this.head = document.getElementsByTagName("head")[0];
11493 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11495 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11497 * @cfg {String} url The URL from which to request the data object.
11500 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11504 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11505 * the server the name of the callback function set up by the load call to process the returned data object.
11506 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11507 * javascript output which calls this named function passing the data object as its only parameter.
11509 callbackParam : "callback",
11511 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11512 * name to the request.
11517 * Load data from the configured URL, read the data object into
11518 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11519 * process that block using the passed callback.
11520 * @param {Object} params An object containing properties which are to be used as HTTP parameters
11521 * for the request to the remote server.
11522 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11523 * object into a block of Roo.data.Records.
11524 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11525 * The function must be passed <ul>
11526 * <li>The Record block object</li>
11527 * <li>The "arg" argument from the load function</li>
11528 * <li>A boolean success indicator</li>
11530 * @param {Object} scope The scope in which to call the callback
11531 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11533 load : function(params, reader, callback, scope, arg){
11534 if(this.fireEvent("beforeload", this, params) !== false){
11536 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11538 var url = this.url;
11539 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11541 url += "&_dc=" + (new Date().getTime());
11543 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11546 cb : "stcCallback"+transId,
11547 scriptId : "stcScript"+transId,
11551 callback : callback,
11557 window[trans.cb] = function(o){
11558 conn.handleResponse(o, trans);
11561 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11563 if(this.autoAbort !== false){
11567 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11569 var script = document.createElement("script");
11570 script.setAttribute("src", url);
11571 script.setAttribute("type", "text/javascript");
11572 script.setAttribute("id", trans.scriptId);
11573 this.head.appendChild(script);
11575 this.trans = trans;
11577 callback.call(scope||this, null, arg, false);
11582 isLoading : function(){
11583 return this.trans ? true : false;
11587 * Abort the current server request.
11589 abort : function(){
11590 if(this.isLoading()){
11591 this.destroyTrans(this.trans);
11596 destroyTrans : function(trans, isLoaded){
11597 this.head.removeChild(document.getElementById(trans.scriptId));
11598 clearTimeout(trans.timeoutId);
11600 window[trans.cb] = undefined;
11602 delete window[trans.cb];
11605 // if hasn't been loaded, wait for load to remove it to prevent script error
11606 window[trans.cb] = function(){
11607 window[trans.cb] = undefined;
11609 delete window[trans.cb];
11616 handleResponse : function(o, trans){
11617 this.trans = false;
11618 this.destroyTrans(trans, true);
11621 result = trans.reader.readRecords(o);
11623 this.fireEvent("loadexception", this, o, trans.arg, e);
11624 trans.callback.call(trans.scope||window, null, trans.arg, false);
11627 this.fireEvent("load", this, o, trans.arg);
11628 trans.callback.call(trans.scope||window, result, trans.arg, true);
11632 handleFailure : function(trans){
11633 this.trans = false;
11634 this.destroyTrans(trans, false);
11635 this.fireEvent("loadexception", this, null, trans.arg);
11636 trans.callback.call(trans.scope||window, null, trans.arg, false);
11640 * Ext JS Library 1.1.1
11641 * Copyright(c) 2006-2007, Ext JS, LLC.
11643 * Originally Released Under LGPL - original licence link has changed is not relivant.
11646 * <script type="text/javascript">
11650 * @class Roo.data.JsonReader
11651 * @extends Roo.data.DataReader
11652 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11653 * based on mappings in a provided Roo.data.Record constructor.
11655 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11656 * in the reply previously.
11661 var RecordDef = Roo.data.Record.create([
11662 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
11663 {name: 'occupation'} // This field will use "occupation" as the mapping.
11665 var myReader = new Roo.data.JsonReader({
11666 totalProperty: "results", // The property which contains the total dataset size (optional)
11667 root: "rows", // The property which contains an Array of row objects
11668 id: "id" // The property within each row object that provides an ID for the record (optional)
11672 * This would consume a JSON file like this:
11674 { 'results': 2, 'rows': [
11675 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11676 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11679 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11680 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11681 * paged from the remote server.
11682 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11683 * @cfg {String} root name of the property which contains the Array of row objects.
11684 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11685 * @cfg {Array} fields Array of field definition objects
11687 * Create a new JsonReader
11688 * @param {Object} meta Metadata configuration options
11689 * @param {Object} recordType Either an Array of field definition objects,
11690 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11692 Roo.data.JsonReader = function(meta, recordType){
11695 // set some defaults:
11696 Roo.applyIf(meta, {
11697 totalProperty: 'total',
11698 successProperty : 'success',
11703 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11705 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11708 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
11709 * Used by Store query builder to append _requestMeta to params.
11712 metaFromRemote : false,
11714 * This method is only used by a DataProxy which has retrieved data from a remote server.
11715 * @param {Object} response The XHR object which contains the JSON data in its responseText.
11716 * @return {Object} data A data block which is used by an Roo.data.Store object as
11717 * a cache of Roo.data.Records.
11719 read : function(response){
11720 var json = response.responseText;
11722 var o = /* eval:var:o */ eval("("+json+")");
11724 throw {message: "JsonReader.read: Json object not found"};
11730 this.metaFromRemote = true;
11731 this.meta = o.metaData;
11732 this.recordType = Roo.data.Record.create(o.metaData.fields);
11733 this.onMetaChange(this.meta, this.recordType, o);
11735 return this.readRecords(o);
11738 // private function a store will implement
11739 onMetaChange : function(meta, recordType, o){
11746 simpleAccess: function(obj, subsc) {
11753 getJsonAccessor: function(){
11755 return function(expr) {
11757 return(re.test(expr))
11758 ? new Function("obj", "return obj." + expr)
11763 return Roo.emptyFn;
11768 * Create a data block containing Roo.data.Records from an XML document.
11769 * @param {Object} o An object which contains an Array of row objects in the property specified
11770 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11771 * which contains the total size of the dataset.
11772 * @return {Object} data A data block which is used by an Roo.data.Store object as
11773 * a cache of Roo.data.Records.
11775 readRecords : function(o){
11777 * After any data loads, the raw JSON data is available for further custom processing.
11781 var s = this.meta, Record = this.recordType,
11782 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11784 // Generate extraction functions for the totalProperty, the root, the id, and for each field
11786 if(s.totalProperty) {
11787 this.getTotal = this.getJsonAccessor(s.totalProperty);
11789 if(s.successProperty) {
11790 this.getSuccess = this.getJsonAccessor(s.successProperty);
11792 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11794 var g = this.getJsonAccessor(s.id);
11795 this.getId = function(rec) {
11797 return (r === undefined || r === "") ? null : r;
11800 this.getId = function(){return null;};
11803 for(var jj = 0; jj < fl; jj++){
11805 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11806 this.ef[jj] = this.getJsonAccessor(map);
11810 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11811 if(s.totalProperty){
11812 var vt = parseInt(this.getTotal(o), 10);
11817 if(s.successProperty){
11818 var vs = this.getSuccess(o);
11819 if(vs === false || vs === 'false'){
11824 for(var i = 0; i < c; i++){
11827 var id = this.getId(n);
11828 for(var j = 0; j < fl; j++){
11830 var v = this.ef[j](n);
11832 Roo.log('missing convert for ' + f.name);
11836 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11838 var record = new Record(values, id);
11840 records[i] = record;
11846 totalRecords : totalRecords
11851 * Ext JS Library 1.1.1
11852 * Copyright(c) 2006-2007, Ext JS, LLC.
11854 * Originally Released Under LGPL - original licence link has changed is not relivant.
11857 * <script type="text/javascript">
11861 * @class Roo.data.ArrayReader
11862 * @extends Roo.data.DataReader
11863 * Data reader class to create an Array of Roo.data.Record objects from an Array.
11864 * Each element of that Array represents a row of data fields. The
11865 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11866 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11870 var RecordDef = Roo.data.Record.create([
11871 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
11872 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
11874 var myReader = new Roo.data.ArrayReader({
11875 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
11879 * This would consume an Array like this:
11881 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11883 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11885 * Create a new JsonReader
11886 * @param {Object} meta Metadata configuration options.
11887 * @param {Object} recordType Either an Array of field definition objects
11888 * as specified to {@link Roo.data.Record#create},
11889 * or an {@link Roo.data.Record} object
11890 * created using {@link Roo.data.Record#create}.
11892 Roo.data.ArrayReader = function(meta, recordType){
11893 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11896 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11898 * Create a data block containing Roo.data.Records from an XML document.
11899 * @param {Object} o An Array of row objects which represents the dataset.
11900 * @return {Object} data A data block which is used by an Roo.data.Store object as
11901 * a cache of Roo.data.Records.
11903 readRecords : function(o){
11904 var sid = this.meta ? this.meta.id : null;
11905 var recordType = this.recordType, fields = recordType.prototype.fields;
11908 for(var i = 0; i < root.length; i++){
11911 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11912 for(var j = 0, jlen = fields.length; j < jlen; j++){
11913 var f = fields.items[j];
11914 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11915 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11917 values[f.name] = v;
11919 var record = new recordType(values, id);
11921 records[records.length] = record;
11925 totalRecords : records.length
11934 * @class Roo.bootstrap.ComboBox
11935 * @extends Roo.bootstrap.TriggerField
11936 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11937 * @cfg {Boolean} append (true|false) default false
11938 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11939 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11940 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11941 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11942 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11943 * @cfg {Boolean} animate default true
11944 * @cfg {Boolean} emptyResultText only for touch device
11945 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11947 * Create a new ComboBox.
11948 * @param {Object} config Configuration options
11950 Roo.bootstrap.ComboBox = function(config){
11951 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11955 * Fires when the dropdown list is expanded
11956 * @param {Roo.bootstrap.ComboBox} combo This combo box
11961 * Fires when the dropdown list is collapsed
11962 * @param {Roo.bootstrap.ComboBox} combo This combo box
11966 * @event beforeselect
11967 * Fires before a list item is selected. Return false to cancel the selection.
11968 * @param {Roo.bootstrap.ComboBox} combo This combo box
11969 * @param {Roo.data.Record} record The data record returned from the underlying store
11970 * @param {Number} index The index of the selected item in the dropdown list
11972 'beforeselect' : true,
11975 * Fires when a list item is selected
11976 * @param {Roo.bootstrap.ComboBox} combo This combo box
11977 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11978 * @param {Number} index The index of the selected item in the dropdown list
11982 * @event beforequery
11983 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11984 * The event object passed has these properties:
11985 * @param {Roo.bootstrap.ComboBox} combo This combo box
11986 * @param {String} query The query
11987 * @param {Boolean} forceAll true to force "all" query
11988 * @param {Boolean} cancel true to cancel the query
11989 * @param {Object} e The query event object
11991 'beforequery': true,
11994 * Fires when the 'add' icon is pressed (add a listener to enable add button)
11995 * @param {Roo.bootstrap.ComboBox} combo This combo box
12000 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12001 * @param {Roo.bootstrap.ComboBox} combo This combo box
12002 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12007 * Fires when the remove value from the combobox array
12008 * @param {Roo.bootstrap.ComboBox} combo This combo box
12012 * @event afterremove
12013 * Fires when the remove value from the combobox array
12014 * @param {Roo.bootstrap.ComboBox} combo This combo box
12016 'afterremove' : true,
12018 * @event specialfilter
12019 * Fires when specialfilter
12020 * @param {Roo.bootstrap.ComboBox} combo This combo box
12022 'specialfilter' : true,
12025 * Fires when tick the element
12026 * @param {Roo.bootstrap.ComboBox} combo This combo box
12030 * @event touchviewdisplay
12031 * Fires when touch view require special display (default is using displayField)
12032 * @param {Roo.bootstrap.ComboBox} combo This combo box
12033 * @param {Object} cfg set html .
12035 'touchviewdisplay' : true
12040 this.tickItems = [];
12042 this.selectedIndex = -1;
12043 if(this.mode == 'local'){
12044 if(config.queryDelay === undefined){
12045 this.queryDelay = 10;
12047 if(config.minChars === undefined){
12053 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12056 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12057 * rendering into an Roo.Editor, defaults to false)
12060 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12061 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12064 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12067 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12068 * the dropdown list (defaults to undefined, with no header element)
12072 * @cfg {String/Roo.Template} tpl The template to use to render the output
12076 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12078 listWidth: undefined,
12080 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12081 * mode = 'remote' or 'text' if mode = 'local')
12083 displayField: undefined,
12086 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12087 * mode = 'remote' or 'value' if mode = 'local').
12088 * Note: use of a valueField requires the user make a selection
12089 * in order for a value to be mapped.
12091 valueField: undefined,
12093 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12098 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12099 * field's data value (defaults to the underlying DOM element's name)
12101 hiddenName: undefined,
12103 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12107 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12109 selectedClass: 'active',
12112 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12116 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12117 * anchor positions (defaults to 'tl-bl')
12119 listAlign: 'tl-bl?',
12121 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12125 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
12126 * query specified by the allQuery config option (defaults to 'query')
12128 triggerAction: 'query',
12130 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12131 * (defaults to 4, does not apply if editable = false)
12135 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12136 * delay (typeAheadDelay) if it matches a known value (defaults to false)
12140 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12141 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12145 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12146 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
12150 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
12151 * when editable = true (defaults to false)
12153 selectOnFocus:false,
12155 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12157 queryParam: 'query',
12159 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
12160 * when mode = 'remote' (defaults to 'Loading...')
12162 loadingText: 'Loading...',
12164 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12168 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12172 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12173 * traditional select (defaults to true)
12177 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12181 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12185 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12186 * listWidth has a higher value)
12190 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12191 * allow the user to set arbitrary text into the field (defaults to false)
12193 forceSelection:false,
12195 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12196 * if typeAhead = true (defaults to 250)
12198 typeAheadDelay : 250,
12200 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12201 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12203 valueNotFoundText : undefined,
12205 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12207 blockFocus : false,
12210 * @cfg {Boolean} disableClear Disable showing of clear button.
12212 disableClear : false,
12214 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
12216 alwaysQuery : false,
12219 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
12224 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12226 invalidClass : "has-warning",
12229 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12231 validClass : "has-success",
12234 * @cfg {Boolean} specialFilter (true|false) special filter default false
12236 specialFilter : false,
12239 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12241 mobileTouchView : true,
12244 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12246 useNativeIOS : false,
12248 ios_options : false,
12260 btnPosition : 'right',
12261 triggerList : true,
12262 showToggleBtn : true,
12264 emptyResultText: 'Empty',
12265 triggerText : 'Select',
12267 // element that contains real text value.. (when hidden is used..)
12269 getAutoCreate : function()
12274 * Render classic select for iso
12277 if(Roo.isIOS && this.useNativeIOS){
12278 cfg = this.getAutoCreateNativeIOS();
12286 if(Roo.isTouch && this.mobileTouchView){
12287 cfg = this.getAutoCreateTouchView();
12294 if(!this.tickable){
12295 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12300 * ComboBox with tickable selections
12303 var align = this.labelAlign || this.parentLabelAlign();
12306 cls : 'form-group roo-combobox-tickable' //input-group
12311 cls : 'tickable-buttons',
12316 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12317 html : this.triggerText
12323 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12330 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12337 buttons.cn.unshift({
12339 cls: 'roo-select2-search-field-input'
12345 Roo.each(buttons.cn, function(c){
12347 c.cls += ' btn-' + _this.size;
12350 if (_this.disabled) {
12361 cls: 'form-hidden-field'
12365 cls: 'roo-select2-choices',
12369 cls: 'roo-select2-search-field',
12381 cls: 'roo-select2-container input-group roo-select2-container-multi',
12386 // cls: 'typeahead typeahead-long dropdown-menu',
12387 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
12392 if(this.hasFeedback && !this.allowBlank){
12396 cls: 'glyphicon form-control-feedback'
12399 combobox.cn.push(feedback);
12402 if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
12404 // Roo.log("left and has label");
12408 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12409 tooltip : 'This field is required'
12414 cls : 'control-label col-sm-' + this.labelWidth,
12415 html : this.fieldLabel
12419 cls : "col-sm-" + (12 - this.labelWidth),
12427 if(this.indicatorpos == 'right'){
12433 cls : 'control-label col-sm-' + this.labelWidth,
12434 html : this.fieldLabel
12439 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12440 tooltip : 'This field is required'
12443 cls : "col-sm-" + (12 - this.labelWidth),
12454 } else if ( this.fieldLabel.length) {
12455 // Roo.log(" label");
12459 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12460 tooltip : 'This field is required'
12464 //cls : 'input-group-addon',
12465 html : this.fieldLabel
12473 if(this.indicatorpos == 'right'){
12478 //cls : 'input-group-addon',
12479 html : this.fieldLabel
12485 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12486 tooltip : 'This field is required'
12497 // Roo.log(" no label && no align");
12504 ['xs','sm','md','lg'].map(function(size){
12505 if (settings[size]) {
12506 cfg.cls += ' col-' + size + '-' + settings[size];
12514 _initEventsCalled : false,
12517 initEvents: function()
12519 if (this._initEventsCalled) { // as we call render... prevent looping...
12522 this._initEventsCalled = true;
12525 throw "can not find store for combo";
12528 this.store = Roo.factory(this.store, Roo.data);
12530 // if we are building from html. then this element is so complex, that we can not really
12531 // use the rendered HTML.
12532 // so we have to trash and replace the previous code.
12533 if (Roo.XComponent.build_from_html) {
12535 // remove this element....
12536 var e = this.el.dom, k=0;
12537 while (e ) { e = e.previousSibling; ++k;}
12542 this.rendered = false;
12544 this.render(this.parent().getChildContainer(true), k);
12550 if(Roo.isIOS && this.useNativeIOS){
12551 this.initIOSView();
12559 if(Roo.isTouch && this.mobileTouchView){
12560 this.initTouchView();
12565 this.initTickableEvents();
12569 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
12571 if(this.hiddenName){
12573 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12575 this.hiddenField.dom.value =
12576 this.hiddenValue !== undefined ? this.hiddenValue :
12577 this.value !== undefined ? this.value : '';
12579 // prevent input submission
12580 this.el.dom.removeAttribute('name');
12581 this.hiddenField.dom.setAttribute('name', this.hiddenName);
12586 // this.el.dom.setAttribute('autocomplete', 'off');
12589 var cls = 'x-combo-list';
12591 //this.list = new Roo.Layer({
12592 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12598 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12599 _this.list.setWidth(lw);
12602 this.list.on('mouseover', this.onViewOver, this);
12603 this.list.on('mousemove', this.onViewMove, this);
12605 this.list.on('scroll', this.onViewScroll, this);
12608 this.list.swallowEvent('mousewheel');
12609 this.assetHeight = 0;
12612 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12613 this.assetHeight += this.header.getHeight();
12616 this.innerList = this.list.createChild({cls:cls+'-inner'});
12617 this.innerList.on('mouseover', this.onViewOver, this);
12618 this.innerList.on('mousemove', this.onViewMove, this);
12619 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12621 if(this.allowBlank && !this.pageSize && !this.disableClear){
12622 this.footer = this.list.createChild({cls:cls+'-ft'});
12623 this.pageTb = new Roo.Toolbar(this.footer);
12627 this.footer = this.list.createChild({cls:cls+'-ft'});
12628 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12629 {pageSize: this.pageSize});
12633 if (this.pageTb && this.allowBlank && !this.disableClear) {
12635 this.pageTb.add(new Roo.Toolbar.Fill(), {
12636 cls: 'x-btn-icon x-btn-clear',
12638 handler: function()
12641 _this.clearValue();
12642 _this.onSelect(false, -1);
12647 this.assetHeight += this.footer.getHeight();
12652 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12655 this.view = new Roo.View(this.list, this.tpl, {
12656 singleSelect:true, store: this.store, selectedClass: this.selectedClass
12658 //this.view.wrapEl.setDisplayed(false);
12659 this.view.on('click', this.onViewClick, this);
12663 this.store.on('beforeload', this.onBeforeLoad, this);
12664 this.store.on('load', this.onLoad, this);
12665 this.store.on('loadexception', this.onLoadException, this);
12667 if(this.resizable){
12668 this.resizer = new Roo.Resizable(this.list, {
12669 pinned:true, handles:'se'
12671 this.resizer.on('resize', function(r, w, h){
12672 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12673 this.listWidth = w;
12674 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12675 this.restrictHeight();
12677 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12680 if(!this.editable){
12681 this.editable = true;
12682 this.setEditable(false);
12687 if (typeof(this.events.add.listeners) != 'undefined') {
12689 this.addicon = this.wrap.createChild(
12690 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
12692 this.addicon.on('click', function(e) {
12693 this.fireEvent('add', this);
12696 if (typeof(this.events.edit.listeners) != 'undefined') {
12698 this.editicon = this.wrap.createChild(
12699 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
12700 if (this.addicon) {
12701 this.editicon.setStyle('margin-left', '40px');
12703 this.editicon.on('click', function(e) {
12705 // we fire even if inothing is selected..
12706 this.fireEvent('edit', this, this.lastData );
12712 this.keyNav = new Roo.KeyNav(this.inputEl(), {
12713 "up" : function(e){
12714 this.inKeyMode = true;
12718 "down" : function(e){
12719 if(!this.isExpanded()){
12720 this.onTriggerClick();
12722 this.inKeyMode = true;
12727 "enter" : function(e){
12728 // this.onViewClick();
12732 if(this.fireEvent("specialkey", this, e)){
12733 this.onViewClick(false);
12739 "esc" : function(e){
12743 "tab" : function(e){
12746 if(this.fireEvent("specialkey", this, e)){
12747 this.onViewClick(false);
12755 doRelay : function(foo, bar, hname){
12756 if(hname == 'down' || this.scope.isExpanded()){
12757 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12766 this.queryDelay = Math.max(this.queryDelay || 10,
12767 this.mode == 'local' ? 10 : 250);
12770 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12772 if(this.typeAhead){
12773 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12775 if(this.editable !== false){
12776 this.inputEl().on("keyup", this.onKeyUp, this);
12778 if(this.forceSelection){
12779 this.inputEl().on('blur', this.doForce, this);
12783 this.choices = this.el.select('ul.roo-select2-choices', true).first();
12784 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12788 initTickableEvents: function()
12792 if(this.hiddenName){
12794 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12796 this.hiddenField.dom.value =
12797 this.hiddenValue !== undefined ? this.hiddenValue :
12798 this.value !== undefined ? this.value : '';
12800 // prevent input submission
12801 this.el.dom.removeAttribute('name');
12802 this.hiddenField.dom.setAttribute('name', this.hiddenName);
12807 // this.list = this.el.select('ul.dropdown-menu',true).first();
12809 this.choices = this.el.select('ul.roo-select2-choices', true).first();
12810 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12811 if(this.triggerList){
12812 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12815 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12816 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12818 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12819 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12821 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12822 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12824 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12825 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12826 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12829 this.cancelBtn.hide();
12834 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12835 _this.list.setWidth(lw);
12838 this.list.on('mouseover', this.onViewOver, this);
12839 this.list.on('mousemove', this.onViewMove, this);
12841 this.list.on('scroll', this.onViewScroll, this);
12844 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>';
12847 this.view = new Roo.View(this.list, this.tpl, {
12848 singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12851 //this.view.wrapEl.setDisplayed(false);
12852 this.view.on('click', this.onViewClick, this);
12856 this.store.on('beforeload', this.onBeforeLoad, this);
12857 this.store.on('load', this.onLoad, this);
12858 this.store.on('loadexception', this.onLoadException, this);
12861 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12862 "up" : function(e){
12863 this.inKeyMode = true;
12867 "down" : function(e){
12868 this.inKeyMode = true;
12872 "enter" : function(e){
12873 if(this.fireEvent("specialkey", this, e)){
12874 this.onViewClick(false);
12880 "esc" : function(e){
12881 this.onTickableFooterButtonClick(e, false, false);
12884 "tab" : function(e){
12885 this.fireEvent("specialkey", this, e);
12887 this.onTickableFooterButtonClick(e, false, false);
12894 doRelay : function(e, fn, key){
12895 if(this.scope.isExpanded()){
12896 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12905 this.queryDelay = Math.max(this.queryDelay || 10,
12906 this.mode == 'local' ? 10 : 250);
12909 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12911 if(this.typeAhead){
12912 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12915 if(this.editable !== false){
12916 this.tickableInputEl().on("keyup", this.onKeyUp, this);
12921 onDestroy : function(){
12923 this.view.setStore(null);
12924 this.view.el.removeAllListeners();
12925 this.view.el.remove();
12926 this.view.purgeListeners();
12929 this.list.dom.innerHTML = '';
12933 this.store.un('beforeload', this.onBeforeLoad, this);
12934 this.store.un('load', this.onLoad, this);
12935 this.store.un('loadexception', this.onLoadException, this);
12937 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12941 fireKey : function(e){
12942 if(e.isNavKeyPress() && !this.list.isVisible()){
12943 this.fireEvent("specialkey", this, e);
12948 onResize: function(w, h){
12949 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12951 // if(typeof w != 'number'){
12952 // // we do not handle it!?!?
12955 // var tw = this.trigger.getWidth();
12956 // // tw += this.addicon ? this.addicon.getWidth() : 0;
12957 // // tw += this.editicon ? this.editicon.getWidth() : 0;
12959 // this.inputEl().setWidth( this.adjustWidth('input', x));
12961 // //this.trigger.setStyle('left', x+'px');
12963 // if(this.list && this.listWidth === undefined){
12964 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12965 // this.list.setWidth(lw);
12966 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12974 * Allow or prevent the user from directly editing the field text. If false is passed,
12975 * the user will only be able to select from the items defined in the dropdown list. This method
12976 * is the runtime equivalent of setting the 'editable' config option at config time.
12977 * @param {Boolean} value True to allow the user to directly edit the field text
12979 setEditable : function(value){
12980 if(value == this.editable){
12983 this.editable = value;
12985 this.inputEl().dom.setAttribute('readOnly', true);
12986 this.inputEl().on('mousedown', this.onTriggerClick, this);
12987 this.inputEl().addClass('x-combo-noedit');
12989 this.inputEl().dom.setAttribute('readOnly', false);
12990 this.inputEl().un('mousedown', this.onTriggerClick, this);
12991 this.inputEl().removeClass('x-combo-noedit');
12997 onBeforeLoad : function(combo,opts){
12998 if(!this.hasFocus){
13002 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13004 this.restrictHeight();
13005 this.selectedIndex = -1;
13009 onLoad : function(){
13011 this.hasQuery = false;
13013 if(!this.hasFocus){
13017 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13018 this.loading.hide();
13021 if(this.store.getCount() > 0){
13023 this.restrictHeight();
13024 if(this.lastQuery == this.allQuery){
13025 if(this.editable && !this.tickable){
13026 this.inputEl().dom.select();
13030 !this.selectByValue(this.value, true) &&
13033 !this.store.lastOptions ||
13034 typeof(this.store.lastOptions.add) == 'undefined' ||
13035 this.store.lastOptions.add != true
13038 this.select(0, true);
13041 if(this.autoFocus){
13044 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13045 this.taTask.delay(this.typeAheadDelay);
13049 this.onEmptyResults();
13055 onLoadException : function()
13057 this.hasQuery = false;
13059 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13060 this.loading.hide();
13063 if(this.tickable && this.editable){
13068 // only causes errors at present
13069 //Roo.log(this.store.reader.jsonData);
13070 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13072 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13078 onTypeAhead : function(){
13079 if(this.store.getCount() > 0){
13080 var r = this.store.getAt(0);
13081 var newValue = r.data[this.displayField];
13082 var len = newValue.length;
13083 var selStart = this.getRawValue().length;
13085 if(selStart != len){
13086 this.setRawValue(newValue);
13087 this.selectText(selStart, newValue.length);
13093 onSelect : function(record, index){
13095 if(this.fireEvent('beforeselect', this, record, index) !== false){
13097 this.setFromData(index > -1 ? record.data : false);
13100 this.fireEvent('select', this, record, index);
13105 * Returns the currently selected field value or empty string if no value is set.
13106 * @return {String} value The selected value
13108 getValue : function()
13110 if(Roo.isIOS && this.useNativeIOS){
13111 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13115 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13118 if(this.valueField){
13119 return typeof this.value != 'undefined' ? this.value : '';
13121 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13125 getRawValue : function()
13127 if(Roo.isIOS && this.useNativeIOS){
13128 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13131 var v = this.inputEl().getValue();
13137 * Clears any text/value currently set in the field
13139 clearValue : function(){
13141 if(this.hiddenField){
13142 this.hiddenField.dom.value = '';
13145 this.setRawValue('');
13146 this.lastSelectionText = '';
13147 this.lastData = false;
13149 var close = this.closeTriggerEl();
13160 * Sets the specified value into the field. If the value finds a match, the corresponding record text
13161 * will be displayed in the field. If the value does not match the data value of an existing item,
13162 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13163 * Otherwise the field will be blank (although the value will still be set).
13164 * @param {String} value The value to match
13166 setValue : function(v)
13168 if(Roo.isIOS && this.useNativeIOS){
13169 this.setIOSValue(v);
13179 if(this.valueField){
13180 var r = this.findRecord(this.valueField, v);
13182 text = r.data[this.displayField];
13183 }else if(this.valueNotFoundText !== undefined){
13184 text = this.valueNotFoundText;
13187 this.lastSelectionText = text;
13188 if(this.hiddenField){
13189 this.hiddenField.dom.value = v;
13191 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13194 var close = this.closeTriggerEl();
13197 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13203 * @property {Object} the last set data for the element
13208 * Sets the value of the field based on a object which is related to the record format for the store.
13209 * @param {Object} value the value to set as. or false on reset?
13211 setFromData : function(o){
13218 var dv = ''; // display value
13219 var vv = ''; // value value..
13221 if (this.displayField) {
13222 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13224 // this is an error condition!!!
13225 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
13228 if(this.valueField){
13229 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13232 var close = this.closeTriggerEl();
13235 (vv.length || vv * 1 > 0) ? close.show() : close.hide();
13238 if(this.hiddenField){
13239 this.hiddenField.dom.value = vv;
13241 this.lastSelectionText = dv;
13242 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13246 // no hidden field.. - we store the value in 'value', but still display
13247 // display field!!!!
13248 this.lastSelectionText = dv;
13249 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13256 reset : function(){
13257 // overridden so that last data is reset..
13264 this.setValue(this.originalValue);
13265 //this.clearInvalid();
13266 this.lastData = false;
13268 this.view.clearSelections();
13274 findRecord : function(prop, value){
13276 if(this.store.getCount() > 0){
13277 this.store.each(function(r){
13278 if(r.data[prop] == value){
13288 getName: function()
13290 // returns hidden if it's set..
13291 if (!this.rendered) {return ''};
13292 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
13296 onViewMove : function(e, t){
13297 this.inKeyMode = false;
13301 onViewOver : function(e, t){
13302 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13305 var item = this.view.findItemFromChild(t);
13308 var index = this.view.indexOf(item);
13309 this.select(index, false);
13314 onViewClick : function(view, doFocus, el, e)
13316 var index = this.view.getSelectedIndexes()[0];
13318 var r = this.store.getAt(index);
13322 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13329 Roo.each(this.tickItems, function(v,k){
13331 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13333 _this.tickItems.splice(k, 1);
13335 if(typeof(e) == 'undefined' && view == false){
13336 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13348 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13349 this.tickItems.push(r.data);
13352 if(typeof(e) == 'undefined' && view == false){
13353 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13360 this.onSelect(r, index);
13362 if(doFocus !== false && !this.blockFocus){
13363 this.inputEl().focus();
13368 restrictHeight : function(){
13369 //this.innerList.dom.style.height = '';
13370 //var inner = this.innerList.dom;
13371 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13372 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13373 //this.list.beginUpdate();
13374 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13375 this.list.alignTo(this.inputEl(), this.listAlign);
13376 this.list.alignTo(this.inputEl(), this.listAlign);
13377 //this.list.endUpdate();
13381 onEmptyResults : function(){
13383 if(this.tickable && this.editable){
13384 this.restrictHeight();
13392 * Returns true if the dropdown list is expanded, else false.
13394 isExpanded : function(){
13395 return this.list.isVisible();
13399 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13400 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13401 * @param {String} value The data value of the item to select
13402 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13403 * selected item if it is not currently in view (defaults to true)
13404 * @return {Boolean} True if the value matched an item in the list, else false
13406 selectByValue : function(v, scrollIntoView){
13407 if(v !== undefined && v !== null){
13408 var r = this.findRecord(this.valueField || this.displayField, v);
13410 this.select(this.store.indexOf(r), scrollIntoView);
13418 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13419 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13420 * @param {Number} index The zero-based index of the list item to select
13421 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13422 * selected item if it is not currently in view (defaults to true)
13424 select : function(index, scrollIntoView){
13425 this.selectedIndex = index;
13426 this.view.select(index);
13427 if(scrollIntoView !== false){
13428 var el = this.view.getNode(index);
13430 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13433 this.list.scrollChildIntoView(el, false);
13439 selectNext : function(){
13440 var ct = this.store.getCount();
13442 if(this.selectedIndex == -1){
13444 }else if(this.selectedIndex < ct-1){
13445 this.select(this.selectedIndex+1);
13451 selectPrev : function(){
13452 var ct = this.store.getCount();
13454 if(this.selectedIndex == -1){
13456 }else if(this.selectedIndex != 0){
13457 this.select(this.selectedIndex-1);
13463 onKeyUp : function(e){
13464 if(this.editable !== false && !e.isSpecialKey()){
13465 this.lastKey = e.getKey();
13466 this.dqTask.delay(this.queryDelay);
13471 validateBlur : function(){
13472 return !this.list || !this.list.isVisible();
13476 initQuery : function(){
13478 var v = this.getRawValue();
13480 if(this.tickable && this.editable){
13481 v = this.tickableInputEl().getValue();
13488 doForce : function(){
13489 if(this.inputEl().dom.value.length > 0){
13490 this.inputEl().dom.value =
13491 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
13497 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
13498 * query allowing the query action to be canceled if needed.
13499 * @param {String} query The SQL query to execute
13500 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
13501 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
13502 * saved in the current store (defaults to false)
13504 doQuery : function(q, forceAll){
13506 if(q === undefined || q === null){
13511 forceAll: forceAll,
13515 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
13520 forceAll = qe.forceAll;
13521 if(forceAll === true || (q.length >= this.minChars)){
13523 this.hasQuery = true;
13525 if(this.lastQuery != q || this.alwaysQuery){
13526 this.lastQuery = q;
13527 if(this.mode == 'local'){
13528 this.selectedIndex = -1;
13530 this.store.clearFilter();
13533 if(this.specialFilter){
13534 this.fireEvent('specialfilter', this);
13539 this.store.filter(this.displayField, q);
13542 this.store.fireEvent("datachanged", this.store);
13549 this.store.baseParams[this.queryParam] = q;
13551 var options = {params : this.getParams(q)};
13554 options.add = true;
13555 options.params.start = this.page * this.pageSize;
13558 this.store.load(options);
13561 * this code will make the page width larger, at the beginning, the list not align correctly,
13562 * we should expand the list on onLoad
13563 * so command out it
13568 this.selectedIndex = -1;
13573 this.loadNext = false;
13577 getParams : function(q){
13579 //p[this.queryParam] = q;
13583 p.limit = this.pageSize;
13589 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
13591 collapse : function(){
13592 if(!this.isExpanded()){
13599 this.hasFocus = false;
13601 this.cancelBtn.hide();
13602 this.trigger.show();
13605 this.tickableInputEl().dom.value = '';
13606 this.tickableInputEl().blur();
13611 Roo.get(document).un('mousedown', this.collapseIf, this);
13612 Roo.get(document).un('mousewheel', this.collapseIf, this);
13613 if (!this.editable) {
13614 Roo.get(document).un('keydown', this.listKeyPress, this);
13616 this.fireEvent('collapse', this);
13622 collapseIf : function(e){
13623 var in_combo = e.within(this.el);
13624 var in_list = e.within(this.list);
13625 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13627 if (in_combo || in_list || is_list) {
13628 //e.stopPropagation();
13633 this.onTickableFooterButtonClick(e, false, false);
13641 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13643 expand : function(){
13645 if(this.isExpanded() || !this.hasFocus){
13649 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13650 this.list.setWidth(lw);
13657 this.restrictHeight();
13661 this.tickItems = Roo.apply([], this.item);
13664 this.cancelBtn.show();
13665 this.trigger.hide();
13668 this.tickableInputEl().focus();
13673 Roo.get(document).on('mousedown', this.collapseIf, this);
13674 Roo.get(document).on('mousewheel', this.collapseIf, this);
13675 if (!this.editable) {
13676 Roo.get(document).on('keydown', this.listKeyPress, this);
13679 this.fireEvent('expand', this);
13683 // Implements the default empty TriggerField.onTriggerClick function
13684 onTriggerClick : function(e)
13686 Roo.log('trigger click');
13688 if(this.disabled || !this.triggerList){
13693 this.loadNext = false;
13695 if(this.isExpanded()){
13697 if (!this.blockFocus) {
13698 this.inputEl().focus();
13702 this.hasFocus = true;
13703 if(this.triggerAction == 'all') {
13704 this.doQuery(this.allQuery, true);
13706 this.doQuery(this.getRawValue());
13708 if (!this.blockFocus) {
13709 this.inputEl().focus();
13714 onTickableTriggerClick : function(e)
13721 this.loadNext = false;
13722 this.hasFocus = true;
13724 if(this.triggerAction == 'all') {
13725 this.doQuery(this.allQuery, true);
13727 this.doQuery(this.getRawValue());
13731 onSearchFieldClick : function(e)
13733 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13734 this.onTickableFooterButtonClick(e, false, false);
13738 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13743 this.loadNext = false;
13744 this.hasFocus = true;
13746 if(this.triggerAction == 'all') {
13747 this.doQuery(this.allQuery, true);
13749 this.doQuery(this.getRawValue());
13753 listKeyPress : function(e)
13755 //Roo.log('listkeypress');
13756 // scroll to first matching element based on key pres..
13757 if (e.isSpecialKey()) {
13760 var k = String.fromCharCode(e.getKey()).toUpperCase();
13763 var csel = this.view.getSelectedNodes();
13764 var cselitem = false;
13766 var ix = this.view.indexOf(csel[0]);
13767 cselitem = this.store.getAt(ix);
13768 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13774 this.store.each(function(v) {
13776 // start at existing selection.
13777 if (cselitem.id == v.id) {
13783 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13784 match = this.store.indexOf(v);
13790 if (match === false) {
13791 return true; // no more action?
13794 this.view.select(match);
13795 var sn = Roo.get(this.view.getSelectedNodes()[0]);
13796 sn.scrollIntoView(sn.dom.parentNode, false);
13799 onViewScroll : function(e, t){
13801 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){
13805 this.hasQuery = true;
13807 this.loading = this.list.select('.loading', true).first();
13809 if(this.loading === null){
13810 this.list.createChild({
13812 cls: 'loading roo-select2-more-results roo-select2-active',
13813 html: 'Loading more results...'
13816 this.loading = this.list.select('.loading', true).first();
13818 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13820 this.loading.hide();
13823 this.loading.show();
13828 this.loadNext = true;
13830 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13835 addItem : function(o)
13837 var dv = ''; // display value
13839 if (this.displayField) {
13840 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13842 // this is an error condition!!!
13843 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
13850 var choice = this.choices.createChild({
13852 cls: 'roo-select2-search-choice',
13861 cls: 'roo-select2-search-choice-close',
13866 }, this.searchField);
13868 var close = choice.select('a.roo-select2-search-choice-close', true).first();
13870 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13878 this.inputEl().dom.value = '';
13883 onRemoveItem : function(e, _self, o)
13885 e.preventDefault();
13887 this.lastItem = Roo.apply([], this.item);
13889 var index = this.item.indexOf(o.data) * 1;
13892 Roo.log('not this item?!');
13896 this.item.splice(index, 1);
13901 this.fireEvent('remove', this, e);
13907 syncValue : function()
13909 if(!this.item.length){
13916 Roo.each(this.item, function(i){
13917 if(_this.valueField){
13918 value.push(i[_this.valueField]);
13925 this.value = value.join(',');
13927 if(this.hiddenField){
13928 this.hiddenField.dom.value = this.value;
13931 this.store.fireEvent("datachanged", this.store);
13936 clearItem : function()
13938 if(!this.multiple){
13944 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
13952 if(this.tickable && !Roo.isTouch){
13953 this.view.refresh();
13957 inputEl: function ()
13959 if(Roo.isIOS && this.useNativeIOS){
13960 return this.el.select('select.roo-ios-select', true).first();
13963 if(Roo.isTouch && this.mobileTouchView){
13964 return this.el.select('input.form-control',true).first();
13968 return this.searchField;
13971 return this.el.select('input.form-control',true).first();
13974 onTickableFooterButtonClick : function(e, btn, el)
13976 e.preventDefault();
13978 this.lastItem = Roo.apply([], this.item);
13980 if(btn && btn.name == 'cancel'){
13981 this.tickItems = Roo.apply([], this.item);
13990 Roo.each(this.tickItems, function(o){
13998 validate : function()
14000 var v = this.getRawValue();
14003 v = this.getValue();
14006 if(this.disabled || this.allowBlank || v.length){
14011 this.markInvalid();
14015 tickableInputEl : function()
14017 if(!this.tickable || !this.editable){
14018 return this.inputEl();
14021 return this.inputEl().select('.roo-select2-search-field-input', true).first();
14025 getAutoCreateTouchView : function()
14030 cls: 'form-group' //input-group
14036 type : this.inputType,
14037 cls : 'form-control x-combo-noedit',
14038 autocomplete: 'new-password',
14039 placeholder : this.placeholder || '',
14044 input.name = this.name;
14048 input.cls += ' input-' + this.size;
14051 if (this.disabled) {
14052 input.disabled = true;
14063 inputblock.cls += ' input-group';
14065 inputblock.cn.unshift({
14067 cls : 'input-group-addon',
14072 if(this.removable && !this.multiple){
14073 inputblock.cls += ' roo-removable';
14075 inputblock.cn.push({
14078 cls : 'roo-combo-removable-btn close'
14082 if(this.hasFeedback && !this.allowBlank){
14084 inputblock.cls += ' has-feedback';
14086 inputblock.cn.push({
14088 cls: 'glyphicon form-control-feedback'
14095 inputblock.cls += (this.before) ? '' : ' input-group';
14097 inputblock.cn.push({
14099 cls : 'input-group-addon',
14110 cls: 'form-hidden-field'
14124 cls: 'form-hidden-field'
14128 cls: 'roo-select2-choices',
14132 cls: 'roo-select2-search-field',
14145 cls: 'roo-select2-container input-group roo-touchview-combobox ',
14151 if(!this.multiple && this.showToggleBtn){
14158 if (this.caret != false) {
14161 cls: 'fa fa-' + this.caret
14168 cls : 'input-group-addon btn dropdown-toggle',
14173 cls: 'combobox-clear',
14187 combobox.cls += ' roo-select2-container-multi';
14190 var align = this.labelAlign || this.parentLabelAlign();
14194 if(this.fieldLabel.length && this.labelWidth){
14196 var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
14197 var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
14202 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14203 tooltip : 'This field is required'
14207 cls : 'control-label ' + lw,
14208 html : this.fieldLabel
14219 if(this.indicatorpos == 'right'){
14223 cls : 'control-label ' + lw,
14224 html : this.fieldLabel
14229 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14230 tooltip : 'This field is required'
14242 var settings = this;
14244 ['xs','sm','md','lg'].map(function(size){
14245 if (settings[size]) {
14246 cfg.cls += ' col-' + size + '-' + settings[size];
14253 initTouchView : function()
14255 this.renderTouchView();
14257 this.touchViewEl.on('scroll', function(){
14258 this.el.dom.scrollTop = 0;
14261 this.originalValue = this.getValue();
14263 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14265 this.inputEl().on("click", this.showTouchView, this);
14266 if (this.triggerEl) {
14267 this.triggerEl.on("click", this.showTouchView, this);
14271 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14272 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14274 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14276 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14277 this.store.on('load', this.onTouchViewLoad, this);
14278 this.store.on('loadexception', this.onTouchViewLoadException, this);
14280 if(this.hiddenName){
14282 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14284 this.hiddenField.dom.value =
14285 this.hiddenValue !== undefined ? this.hiddenValue :
14286 this.value !== undefined ? this.value : '';
14288 this.el.dom.removeAttribute('name');
14289 this.hiddenField.dom.setAttribute('name', this.hiddenName);
14293 this.choices = this.el.select('ul.roo-select2-choices', true).first();
14294 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14297 if(this.removable && !this.multiple){
14298 var close = this.closeTriggerEl();
14300 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14301 close.on('click', this.removeBtnClick, this, close);
14305 * fix the bug in Safari iOS8
14307 this.inputEl().on("focus", function(e){
14308 document.activeElement.blur();
14316 renderTouchView : function()
14318 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14319 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14321 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14322 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14324 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14325 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14326 this.touchViewBodyEl.setStyle('overflow', 'auto');
14328 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14329 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14331 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14332 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14336 showTouchView : function()
14342 this.touchViewHeaderEl.hide();
14344 if(this.modalTitle.length){
14345 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14346 this.touchViewHeaderEl.show();
14349 this.touchViewEl.show();
14351 this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
14352 this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14353 Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14355 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14357 if(this.modalTitle.length){
14358 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14361 this.touchViewBodyEl.setHeight(bodyHeight);
14365 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14367 this.touchViewEl.addClass('in');
14370 this.doTouchViewQuery();
14374 hideTouchView : function()
14376 this.touchViewEl.removeClass('in');
14380 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14382 this.touchViewEl.setStyle('display', 'none');
14387 setTouchViewValue : function()
14394 Roo.each(this.tickItems, function(o){
14399 this.hideTouchView();
14402 doTouchViewQuery : function()
14411 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
14415 if(!this.alwaysQuery || this.mode == 'local'){
14416 this.onTouchViewLoad();
14423 onTouchViewBeforeLoad : function(combo,opts)
14429 onTouchViewLoad : function()
14431 if(this.store.getCount() < 1){
14432 this.onTouchViewEmptyResults();
14436 this.clearTouchView();
14438 var rawValue = this.getRawValue();
14440 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
14442 this.tickItems = [];
14444 this.store.data.each(function(d, rowIndex){
14445 var row = this.touchViewListGroup.createChild(template);
14447 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
14448 row.addClass(d.data.cls);
14451 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14454 html : d.data[this.displayField]
14457 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
14458 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
14461 row.removeClass('selected');
14462 if(!this.multiple && this.valueField &&
14463 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
14466 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14467 row.addClass('selected');
14470 if(this.multiple && this.valueField &&
14471 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
14475 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14476 this.tickItems.push(d.data);
14479 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
14483 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
14485 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14487 if(this.modalTitle.length){
14488 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14491 var listHeight = this.touchViewListGroup.getHeight();
14495 if(firstChecked && listHeight > bodyHeight){
14496 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
14501 onTouchViewLoadException : function()
14503 this.hideTouchView();
14506 onTouchViewEmptyResults : function()
14508 this.clearTouchView();
14510 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
14512 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
14516 clearTouchView : function()
14518 this.touchViewListGroup.dom.innerHTML = '';
14521 onTouchViewClick : function(e, el, o)
14523 e.preventDefault();
14526 var rowIndex = o.rowIndex;
14528 var r = this.store.getAt(rowIndex);
14530 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
14532 if(!this.multiple){
14533 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
14534 c.dom.removeAttribute('checked');
14537 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14539 this.setFromData(r.data);
14541 var close = this.closeTriggerEl();
14547 this.hideTouchView();
14549 this.fireEvent('select', this, r, rowIndex);
14554 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
14555 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
14556 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
14560 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14561 this.addItem(r.data);
14562 this.tickItems.push(r.data);
14566 getAutoCreateNativeIOS : function()
14569 cls: 'form-group' //input-group,
14574 cls : 'roo-ios-select'
14578 combobox.name = this.name;
14581 if (this.disabled) {
14582 combobox.disabled = true;
14585 var settings = this;
14587 ['xs','sm','md','lg'].map(function(size){
14588 if (settings[size]) {
14589 cfg.cls += ' col-' + size + '-' + settings[size];
14599 initIOSView : function()
14601 this.store.on('load', this.onIOSViewLoad, this);
14606 onIOSViewLoad : function()
14608 if(this.store.getCount() < 1){
14612 this.clearIOSView();
14614 if(this.allowBlank) {
14616 var default_text = '-- SELECT --';
14618 var opt = this.inputEl().createChild({
14621 html : default_text
14625 o[this.valueField] = 0;
14626 o[this.displayField] = default_text;
14628 this.ios_options.push({
14635 this.store.data.each(function(d, rowIndex){
14639 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14640 html = d.data[this.displayField];
14645 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
14646 value = d.data[this.valueField];
14655 if(this.value == d.data[this.valueField]){
14656 option['selected'] = true;
14659 var opt = this.inputEl().createChild(option);
14661 this.ios_options.push({
14668 this.inputEl().on('change', function(){
14669 this.fireEvent('select', this);
14674 clearIOSView: function()
14676 this.inputEl().dom.innerHTML = '';
14678 this.ios_options = [];
14681 setIOSValue: function(v)
14685 if(!this.ios_options){
14689 Roo.each(this.ios_options, function(opts){
14691 opts.el.dom.removeAttribute('selected');
14693 if(opts.data[this.valueField] != v){
14697 opts.el.dom.setAttribute('selected', true);
14703 * @cfg {Boolean} grow
14707 * @cfg {Number} growMin
14711 * @cfg {Number} growMax
14720 Roo.apply(Roo.bootstrap.ComboBox, {
14724 cls: 'modal-header',
14746 cls: 'list-group-item',
14750 cls: 'roo-combobox-list-group-item-value'
14754 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
14768 listItemCheckbox : {
14770 cls: 'list-group-item',
14774 cls: 'roo-combobox-list-group-item-value'
14778 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
14794 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
14799 cls: 'modal-footer',
14807 cls: 'col-xs-6 text-left',
14810 cls: 'btn btn-danger roo-touch-view-cancel',
14816 cls: 'col-xs-6 text-right',
14819 cls: 'btn btn-success roo-touch-view-ok',
14830 Roo.apply(Roo.bootstrap.ComboBox, {
14832 touchViewTemplate : {
14834 cls: 'modal fade roo-combobox-touch-view',
14838 cls: 'modal-dialog',
14839 style : 'position:fixed', // we have to fix position....
14843 cls: 'modal-content',
14845 Roo.bootstrap.ComboBox.header,
14846 Roo.bootstrap.ComboBox.body,
14847 Roo.bootstrap.ComboBox.footer
14856 * Ext JS Library 1.1.1
14857 * Copyright(c) 2006-2007, Ext JS, LLC.
14859 * Originally Released Under LGPL - original licence link has changed is not relivant.
14862 * <script type="text/javascript">
14867 * @extends Roo.util.Observable
14868 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
14869 * This class also supports single and multi selection modes. <br>
14870 * Create a data model bound view:
14872 var store = new Roo.data.Store(...);
14874 var view = new Roo.View({
14876 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
14878 singleSelect: true,
14879 selectedClass: "ydataview-selected",
14883 // listen for node click?
14884 view.on("click", function(vw, index, node, e){
14885 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14889 dataModel.load("foobar.xml");
14891 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14893 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14894 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14896 * Note: old style constructor is still suported (container, template, config)
14899 * Create a new View
14900 * @param {Object} config The config object
14903 Roo.View = function(config, depreciated_tpl, depreciated_config){
14905 this.parent = false;
14907 if (typeof(depreciated_tpl) == 'undefined') {
14908 // new way.. - universal constructor.
14909 Roo.apply(this, config);
14910 this.el = Roo.get(this.el);
14913 this.el = Roo.get(config);
14914 this.tpl = depreciated_tpl;
14915 Roo.apply(this, depreciated_config);
14917 this.wrapEl = this.el.wrap().wrap();
14918 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14921 if(typeof(this.tpl) == "string"){
14922 this.tpl = new Roo.Template(this.tpl);
14924 // support xtype ctors..
14925 this.tpl = new Roo.factory(this.tpl, Roo);
14929 this.tpl.compile();
14934 * @event beforeclick
14935 * Fires before a click is processed. Returns false to cancel the default action.
14936 * @param {Roo.View} this
14937 * @param {Number} index The index of the target node
14938 * @param {HTMLElement} node The target node
14939 * @param {Roo.EventObject} e The raw event object
14941 "beforeclick" : true,
14944 * Fires when a template node is clicked.
14945 * @param {Roo.View} this
14946 * @param {Number} index The index of the target node
14947 * @param {HTMLElement} node The target node
14948 * @param {Roo.EventObject} e The raw event object
14953 * Fires when a template node is double clicked.
14954 * @param {Roo.View} this
14955 * @param {Number} index The index of the target node
14956 * @param {HTMLElement} node The target node
14957 * @param {Roo.EventObject} e The raw event object
14961 * @event contextmenu
14962 * Fires when a template node is right clicked.
14963 * @param {Roo.View} this
14964 * @param {Number} index The index of the target node
14965 * @param {HTMLElement} node The target node
14966 * @param {Roo.EventObject} e The raw event object
14968 "contextmenu" : true,
14970 * @event selectionchange
14971 * Fires when the selected nodes change.
14972 * @param {Roo.View} this
14973 * @param {Array} selections Array of the selected nodes
14975 "selectionchange" : true,
14978 * @event beforeselect
14979 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
14980 * @param {Roo.View} this
14981 * @param {HTMLElement} node The node to be selected
14982 * @param {Array} selections Array of currently selected nodes
14984 "beforeselect" : true,
14986 * @event preparedata
14987 * Fires on every row to render, to allow you to change the data.
14988 * @param {Roo.View} this
14989 * @param {Object} data to be rendered (change this)
14991 "preparedata" : true
14999 "click": this.onClick,
15000 "dblclick": this.onDblClick,
15001 "contextmenu": this.onContextMenu,
15005 this.selections = [];
15007 this.cmp = new Roo.CompositeElementLite([]);
15009 this.store = Roo.factory(this.store, Roo.data);
15010 this.setStore(this.store, true);
15013 if ( this.footer && this.footer.xtype) {
15015 var fctr = this.wrapEl.appendChild(document.createElement("div"));
15017 this.footer.dataSource = this.store;
15018 this.footer.container = fctr;
15019 this.footer = Roo.factory(this.footer, Roo);
15020 fctr.insertFirst(this.el);
15022 // this is a bit insane - as the paging toolbar seems to detach the el..
15023 // dom.parentNode.parentNode.parentNode
15024 // they get detached?
15028 Roo.View.superclass.constructor.call(this);
15033 Roo.extend(Roo.View, Roo.util.Observable, {
15036 * @cfg {Roo.data.Store} store Data store to load data from.
15041 * @cfg {String|Roo.Element} el The container element.
15046 * @cfg {String|Roo.Template} tpl The template used by this View
15050 * @cfg {String} dataName the named area of the template to use as the data area
15051 * Works with domtemplates roo-name="name"
15055 * @cfg {String} selectedClass The css class to add to selected nodes
15057 selectedClass : "x-view-selected",
15059 * @cfg {String} emptyText The empty text to show when nothing is loaded.
15064 * @cfg {String} text to display on mask (default Loading)
15068 * @cfg {Boolean} multiSelect Allow multiple selection
15070 multiSelect : false,
15072 * @cfg {Boolean} singleSelect Allow single selection
15074 singleSelect: false,
15077 * @cfg {Boolean} toggleSelect - selecting
15079 toggleSelect : false,
15082 * @cfg {Boolean} tickable - selecting
15087 * Returns the element this view is bound to.
15088 * @return {Roo.Element}
15090 getEl : function(){
15091 return this.wrapEl;
15097 * Refreshes the view. - called by datachanged on the store. - do not call directly.
15099 refresh : function(){
15100 //Roo.log('refresh');
15103 // if we are using something like 'domtemplate', then
15104 // the what gets used is:
15105 // t.applySubtemplate(NAME, data, wrapping data..)
15106 // the outer template then get' applied with
15107 // the store 'extra data'
15108 // and the body get's added to the
15109 // roo-name="data" node?
15110 // <span class='roo-tpl-{name}'></span> ?????
15114 this.clearSelections();
15115 this.el.update("");
15117 var records = this.store.getRange();
15118 if(records.length < 1) {
15120 // is this valid?? = should it render a template??
15122 this.el.update(this.emptyText);
15126 if (this.dataName) {
15127 this.el.update(t.apply(this.store.meta)); //????
15128 el = this.el.child('.roo-tpl-' + this.dataName);
15131 for(var i = 0, len = records.length; i < len; i++){
15132 var data = this.prepareData(records[i].data, i, records[i]);
15133 this.fireEvent("preparedata", this, data, i, records[i]);
15135 var d = Roo.apply({}, data);
15138 Roo.apply(d, {'roo-id' : Roo.id()});
15142 Roo.each(this.parent.item, function(item){
15143 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15146 Roo.apply(d, {'roo-data-checked' : 'checked'});
15150 html[html.length] = Roo.util.Format.trim(
15152 t.applySubtemplate(this.dataName, d, this.store.meta) :
15159 el.update(html.join(""));
15160 this.nodes = el.dom.childNodes;
15161 this.updateIndexes(0);
15166 * Function to override to reformat the data that is sent to
15167 * the template for each node.
15168 * DEPRICATED - use the preparedata event handler.
15169 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15170 * a JSON object for an UpdateManager bound view).
15172 prepareData : function(data, index, record)
15174 this.fireEvent("preparedata", this, data, index, record);
15178 onUpdate : function(ds, record){
15179 // Roo.log('on update');
15180 this.clearSelections();
15181 var index = this.store.indexOf(record);
15182 var n = this.nodes[index];
15183 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
15184 n.parentNode.removeChild(n);
15185 this.updateIndexes(index, index);
15191 onAdd : function(ds, records, index)
15193 //Roo.log(['on Add', ds, records, index] );
15194 this.clearSelections();
15195 if(this.nodes.length == 0){
15199 var n = this.nodes[index];
15200 for(var i = 0, len = records.length; i < len; i++){
15201 var d = this.prepareData(records[i].data, i, records[i]);
15203 this.tpl.insertBefore(n, d);
15206 this.tpl.append(this.el, d);
15209 this.updateIndexes(index);
15212 onRemove : function(ds, record, index){
15213 // Roo.log('onRemove');
15214 this.clearSelections();
15215 var el = this.dataName ?
15216 this.el.child('.roo-tpl-' + this.dataName) :
15219 el.dom.removeChild(this.nodes[index]);
15220 this.updateIndexes(index);
15224 * Refresh an individual node.
15225 * @param {Number} index
15227 refreshNode : function(index){
15228 this.onUpdate(this.store, this.store.getAt(index));
15231 updateIndexes : function(startIndex, endIndex){
15232 var ns = this.nodes;
15233 startIndex = startIndex || 0;
15234 endIndex = endIndex || ns.length - 1;
15235 for(var i = startIndex; i <= endIndex; i++){
15236 ns[i].nodeIndex = i;
15241 * Changes the data store this view uses and refresh the view.
15242 * @param {Store} store
15244 setStore : function(store, initial){
15245 if(!initial && this.store){
15246 this.store.un("datachanged", this.refresh);
15247 this.store.un("add", this.onAdd);
15248 this.store.un("remove", this.onRemove);
15249 this.store.un("update", this.onUpdate);
15250 this.store.un("clear", this.refresh);
15251 this.store.un("beforeload", this.onBeforeLoad);
15252 this.store.un("load", this.onLoad);
15253 this.store.un("loadexception", this.onLoad);
15257 store.on("datachanged", this.refresh, this);
15258 store.on("add", this.onAdd, this);
15259 store.on("remove", this.onRemove, this);
15260 store.on("update", this.onUpdate, this);
15261 store.on("clear", this.refresh, this);
15262 store.on("beforeload", this.onBeforeLoad, this);
15263 store.on("load", this.onLoad, this);
15264 store.on("loadexception", this.onLoad, this);
15272 * onbeforeLoad - masks the loading area.
15275 onBeforeLoad : function(store,opts)
15277 //Roo.log('onBeforeLoad');
15279 this.el.update("");
15281 this.el.mask(this.mask ? this.mask : "Loading" );
15283 onLoad : function ()
15290 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15291 * @param {HTMLElement} node
15292 * @return {HTMLElement} The template node
15294 findItemFromChild : function(node){
15295 var el = this.dataName ?
15296 this.el.child('.roo-tpl-' + this.dataName,true) :
15299 if(!node || node.parentNode == el){
15302 var p = node.parentNode;
15303 while(p && p != el){
15304 if(p.parentNode == el){
15313 onClick : function(e){
15314 var item = this.findItemFromChild(e.getTarget());
15316 var index = this.indexOf(item);
15317 if(this.onItemClick(item, index, e) !== false){
15318 this.fireEvent("click", this, index, item, e);
15321 this.clearSelections();
15326 onContextMenu : function(e){
15327 var item = this.findItemFromChild(e.getTarget());
15329 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15334 onDblClick : function(e){
15335 var item = this.findItemFromChild(e.getTarget());
15337 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15341 onItemClick : function(item, index, e)
15343 if(this.fireEvent("beforeclick", this, index, item, e) === false){
15346 if (this.toggleSelect) {
15347 var m = this.isSelected(item) ? 'unselect' : 'select';
15350 _t[m](item, true, false);
15353 if(this.multiSelect || this.singleSelect){
15354 if(this.multiSelect && e.shiftKey && this.lastSelection){
15355 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
15357 this.select(item, this.multiSelect && e.ctrlKey);
15358 this.lastSelection = item;
15361 if(!this.tickable){
15362 e.preventDefault();
15370 * Get the number of selected nodes.
15373 getSelectionCount : function(){
15374 return this.selections.length;
15378 * Get the currently selected nodes.
15379 * @return {Array} An array of HTMLElements
15381 getSelectedNodes : function(){
15382 return this.selections;
15386 * Get the indexes of the selected nodes.
15389 getSelectedIndexes : function(){
15390 var indexes = [], s = this.selections;
15391 for(var i = 0, len = s.length; i < len; i++){
15392 indexes.push(s[i].nodeIndex);
15398 * Clear all selections
15399 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
15401 clearSelections : function(suppressEvent){
15402 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
15403 this.cmp.elements = this.selections;
15404 this.cmp.removeClass(this.selectedClass);
15405 this.selections = [];
15406 if(!suppressEvent){
15407 this.fireEvent("selectionchange", this, this.selections);
15413 * Returns true if the passed node is selected
15414 * @param {HTMLElement/Number} node The node or node index
15415 * @return {Boolean}
15417 isSelected : function(node){
15418 var s = this.selections;
15422 node = this.getNode(node);
15423 return s.indexOf(node) !== -1;
15428 * @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
15429 * @param {Boolean} keepExisting (optional) true to keep existing selections
15430 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15432 select : function(nodeInfo, keepExisting, suppressEvent){
15433 if(nodeInfo instanceof Array){
15435 this.clearSelections(true);
15437 for(var i = 0, len = nodeInfo.length; i < len; i++){
15438 this.select(nodeInfo[i], true, true);
15442 var node = this.getNode(nodeInfo);
15443 if(!node || this.isSelected(node)){
15444 return; // already selected.
15447 this.clearSelections(true);
15450 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
15451 Roo.fly(node).addClass(this.selectedClass);
15452 this.selections.push(node);
15453 if(!suppressEvent){
15454 this.fireEvent("selectionchange", this, this.selections);
15462 * @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
15463 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
15464 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15466 unselect : function(nodeInfo, keepExisting, suppressEvent)
15468 if(nodeInfo instanceof Array){
15469 Roo.each(this.selections, function(s) {
15470 this.unselect(s, nodeInfo);
15474 var node = this.getNode(nodeInfo);
15475 if(!node || !this.isSelected(node)){
15476 //Roo.log("not selected");
15477 return; // not selected.
15481 Roo.each(this.selections, function(s) {
15483 Roo.fly(node).removeClass(this.selectedClass);
15490 this.selections= ns;
15491 this.fireEvent("selectionchange", this, this.selections);
15495 * Gets a template node.
15496 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15497 * @return {HTMLElement} The node or null if it wasn't found
15499 getNode : function(nodeInfo){
15500 if(typeof nodeInfo == "string"){
15501 return document.getElementById(nodeInfo);
15502 }else if(typeof nodeInfo == "number"){
15503 return this.nodes[nodeInfo];
15509 * Gets a range template nodes.
15510 * @param {Number} startIndex
15511 * @param {Number} endIndex
15512 * @return {Array} An array of nodes
15514 getNodes : function(start, end){
15515 var ns = this.nodes;
15516 start = start || 0;
15517 end = typeof end == "undefined" ? ns.length - 1 : end;
15520 for(var i = start; i <= end; i++){
15524 for(var i = start; i >= end; i--){
15532 * Finds the index of the passed node
15533 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15534 * @return {Number} The index of the node or -1
15536 indexOf : function(node){
15537 node = this.getNode(node);
15538 if(typeof node.nodeIndex == "number"){
15539 return node.nodeIndex;
15541 var ns = this.nodes;
15542 for(var i = 0, len = ns.length; i < len; i++){
15553 * based on jquery fullcalendar
15557 Roo.bootstrap = Roo.bootstrap || {};
15559 * @class Roo.bootstrap.Calendar
15560 * @extends Roo.bootstrap.Component
15561 * Bootstrap Calendar class
15562 * @cfg {Boolean} loadMask (true|false) default false
15563 * @cfg {Object} header generate the user specific header of the calendar, default false
15566 * Create a new Container
15567 * @param {Object} config The config object
15572 Roo.bootstrap.Calendar = function(config){
15573 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
15577 * Fires when a date is selected
15578 * @param {DatePicker} this
15579 * @param {Date} date The selected date
15583 * @event monthchange
15584 * Fires when the displayed month changes
15585 * @param {DatePicker} this
15586 * @param {Date} date The selected month
15588 'monthchange': true,
15590 * @event evententer
15591 * Fires when mouse over an event
15592 * @param {Calendar} this
15593 * @param {event} Event
15595 'evententer': true,
15597 * @event eventleave
15598 * Fires when the mouse leaves an
15599 * @param {Calendar} this
15602 'eventleave': true,
15604 * @event eventclick
15605 * Fires when the mouse click an
15606 * @param {Calendar} this
15615 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
15618 * @cfg {Number} startDay
15619 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
15627 getAutoCreate : function(){
15630 var fc_button = function(name, corner, style, content ) {
15631 return Roo.apply({},{
15633 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
15635 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
15638 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
15649 style : 'width:100%',
15656 cls : 'fc-header-left',
15658 fc_button('prev', 'left', 'arrow', '‹' ),
15659 fc_button('next', 'right', 'arrow', '›' ),
15660 { tag: 'span', cls: 'fc-header-space' },
15661 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
15669 cls : 'fc-header-center',
15673 cls: 'fc-header-title',
15676 html : 'month / year'
15684 cls : 'fc-header-right',
15686 /* fc_button('month', 'left', '', 'month' ),
15687 fc_button('week', '', '', 'week' ),
15688 fc_button('day', 'right', '', 'day' )
15700 header = this.header;
15703 var cal_heads = function() {
15705 // fixme - handle this.
15707 for (var i =0; i < Date.dayNames.length; i++) {
15708 var d = Date.dayNames[i];
15711 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
15712 html : d.substring(0,3)
15716 ret[0].cls += ' fc-first';
15717 ret[6].cls += ' fc-last';
15720 var cal_cell = function(n) {
15723 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
15728 cls: 'fc-day-number',
15732 cls: 'fc-day-content',
15736 style: 'position: relative;' // height: 17px;
15748 var cal_rows = function() {
15751 for (var r = 0; r < 6; r++) {
15758 for (var i =0; i < Date.dayNames.length; i++) {
15759 var d = Date.dayNames[i];
15760 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
15763 row.cn[0].cls+=' fc-first';
15764 row.cn[0].cn[0].style = 'min-height:90px';
15765 row.cn[6].cls+=' fc-last';
15769 ret[0].cls += ' fc-first';
15770 ret[4].cls += ' fc-prev-last';
15771 ret[5].cls += ' fc-last';
15778 cls: 'fc-border-separate',
15779 style : 'width:100%',
15787 cls : 'fc-first fc-last',
15805 cls : 'fc-content',
15806 style : "position: relative;",
15809 cls : 'fc-view fc-view-month fc-grid',
15810 style : 'position: relative',
15811 unselectable : 'on',
15814 cls : 'fc-event-container',
15815 style : 'position:absolute;z-index:8;top:0;left:0;'
15833 initEvents : function()
15836 throw "can not find store for calendar";
15842 style: "text-align:center",
15846 style: "background-color:white;width:50%;margin:250 auto",
15850 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
15861 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15863 var size = this.el.select('.fc-content', true).first().getSize();
15864 this.maskEl.setSize(size.width, size.height);
15865 this.maskEl.enableDisplayMode("block");
15866 if(!this.loadMask){
15867 this.maskEl.hide();
15870 this.store = Roo.factory(this.store, Roo.data);
15871 this.store.on('load', this.onLoad, this);
15872 this.store.on('beforeload', this.onBeforeLoad, this);
15876 this.cells = this.el.select('.fc-day',true);
15877 //Roo.log(this.cells);
15878 this.textNodes = this.el.query('.fc-day-number');
15879 this.cells.addClassOnOver('fc-state-hover');
15881 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15882 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15883 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15884 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15886 this.on('monthchange', this.onMonthChange, this);
15888 this.update(new Date().clearTime());
15891 resize : function() {
15892 var sz = this.el.getSize();
15894 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15895 this.el.select('.fc-day-content div',true).setHeight(34);
15900 showPrevMonth : function(e){
15901 this.update(this.activeDate.add("mo", -1));
15903 showToday : function(e){
15904 this.update(new Date().clearTime());
15907 showNextMonth : function(e){
15908 this.update(this.activeDate.add("mo", 1));
15912 showPrevYear : function(){
15913 this.update(this.activeDate.add("y", -1));
15917 showNextYear : function(){
15918 this.update(this.activeDate.add("y", 1));
15923 update : function(date)
15925 var vd = this.activeDate;
15926 this.activeDate = date;
15927 // if(vd && this.el){
15928 // var t = date.getTime();
15929 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
15930 // Roo.log('using add remove');
15932 // this.fireEvent('monthchange', this, date);
15934 // this.cells.removeClass("fc-state-highlight");
15935 // this.cells.each(function(c){
15936 // if(c.dateValue == t){
15937 // c.addClass("fc-state-highlight");
15938 // setTimeout(function(){
15939 // try{c.dom.firstChild.focus();}catch(e){}
15949 var days = date.getDaysInMonth();
15951 var firstOfMonth = date.getFirstDateOfMonth();
15952 var startingPos = firstOfMonth.getDay()-this.startDay;
15954 if(startingPos < this.startDay){
15958 var pm = date.add(Date.MONTH, -1);
15959 var prevStart = pm.getDaysInMonth()-startingPos;
15961 this.cells = this.el.select('.fc-day',true);
15962 this.textNodes = this.el.query('.fc-day-number');
15963 this.cells.addClassOnOver('fc-state-hover');
15965 var cells = this.cells.elements;
15966 var textEls = this.textNodes;
15968 Roo.each(cells, function(cell){
15969 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
15972 days += startingPos;
15974 // convert everything to numbers so it's fast
15975 var day = 86400000;
15976 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
15979 //Roo.log(prevStart);
15981 var today = new Date().clearTime().getTime();
15982 var sel = date.clearTime().getTime();
15983 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
15984 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
15985 var ddMatch = this.disabledDatesRE;
15986 var ddText = this.disabledDatesText;
15987 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
15988 var ddaysText = this.disabledDaysText;
15989 var format = this.format;
15991 var setCellClass = function(cal, cell){
15995 //Roo.log('set Cell Class');
15997 var t = d.getTime();
16001 cell.dateValue = t;
16003 cell.className += " fc-today";
16004 cell.className += " fc-state-highlight";
16005 cell.title = cal.todayText;
16008 // disable highlight in other month..
16009 //cell.className += " fc-state-highlight";
16014 cell.className = " fc-state-disabled";
16015 cell.title = cal.minText;
16019 cell.className = " fc-state-disabled";
16020 cell.title = cal.maxText;
16024 if(ddays.indexOf(d.getDay()) != -1){
16025 cell.title = ddaysText;
16026 cell.className = " fc-state-disabled";
16029 if(ddMatch && format){
16030 var fvalue = d.dateFormat(format);
16031 if(ddMatch.test(fvalue)){
16032 cell.title = ddText.replace("%0", fvalue);
16033 cell.className = " fc-state-disabled";
16037 if (!cell.initialClassName) {
16038 cell.initialClassName = cell.dom.className;
16041 cell.dom.className = cell.initialClassName + ' ' + cell.className;
16046 for(; i < startingPos; i++) {
16047 textEls[i].innerHTML = (++prevStart);
16048 d.setDate(d.getDate()+1);
16050 cells[i].className = "fc-past fc-other-month";
16051 setCellClass(this, cells[i]);
16056 for(; i < days; i++){
16057 intDay = i - startingPos + 1;
16058 textEls[i].innerHTML = (intDay);
16059 d.setDate(d.getDate()+1);
16061 cells[i].className = ''; // "x-date-active";
16062 setCellClass(this, cells[i]);
16066 for(; i < 42; i++) {
16067 textEls[i].innerHTML = (++extraDays);
16068 d.setDate(d.getDate()+1);
16070 cells[i].className = "fc-future fc-other-month";
16071 setCellClass(this, cells[i]);
16074 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16076 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16078 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16079 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16081 if(totalRows != 6){
16082 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16083 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16086 this.fireEvent('monthchange', this, date);
16090 if(!this.internalRender){
16091 var main = this.el.dom.firstChild;
16092 var w = main.offsetWidth;
16093 this.el.setWidth(w + this.el.getBorderWidth("lr"));
16094 Roo.fly(main).setWidth(w);
16095 this.internalRender = true;
16096 // opera does not respect the auto grow header center column
16097 // then, after it gets a width opera refuses to recalculate
16098 // without a second pass
16099 if(Roo.isOpera && !this.secondPass){
16100 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16101 this.secondPass = true;
16102 this.update.defer(10, this, [date]);
16109 findCell : function(dt) {
16110 dt = dt.clearTime().getTime();
16112 this.cells.each(function(c){
16113 //Roo.log("check " +c.dateValue + '?=' + dt);
16114 if(c.dateValue == dt){
16124 findCells : function(ev) {
16125 var s = ev.start.clone().clearTime().getTime();
16127 var e= ev.end.clone().clearTime().getTime();
16130 this.cells.each(function(c){
16131 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16133 if(c.dateValue > e){
16136 if(c.dateValue < s){
16145 // findBestRow: function(cells)
16149 // for (var i =0 ; i < cells.length;i++) {
16150 // ret = Math.max(cells[i].rows || 0,ret);
16157 addItem : function(ev)
16159 // look for vertical location slot in
16160 var cells = this.findCells(ev);
16162 // ev.row = this.findBestRow(cells);
16164 // work out the location.
16168 for(var i =0; i < cells.length; i++) {
16170 cells[i].row = cells[0].row;
16173 cells[i].row = cells[i].row + 1;
16183 if (crow.start.getY() == cells[i].getY()) {
16185 crow.end = cells[i];
16202 cells[0].events.push(ev);
16204 this.calevents.push(ev);
16207 clearEvents: function() {
16209 if(!this.calevents){
16213 Roo.each(this.cells.elements, function(c){
16219 Roo.each(this.calevents, function(e) {
16220 Roo.each(e.els, function(el) {
16221 el.un('mouseenter' ,this.onEventEnter, this);
16222 el.un('mouseleave' ,this.onEventLeave, this);
16227 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16233 renderEvents: function()
16237 this.cells.each(function(c) {
16246 if(c.row != c.events.length){
16247 r = 4 - (4 - (c.row - c.events.length));
16250 c.events = ev.slice(0, r);
16251 c.more = ev.slice(r);
16253 if(c.more.length && c.more.length == 1){
16254 c.events.push(c.more.pop());
16257 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16261 this.cells.each(function(c) {
16263 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16266 for (var e = 0; e < c.events.length; e++){
16267 var ev = c.events[e];
16268 var rows = ev.rows;
16270 for(var i = 0; i < rows.length; i++) {
16272 // how many rows should it span..
16275 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16276 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16278 unselectable : "on",
16281 cls: 'fc-event-inner',
16285 // cls: 'fc-event-time',
16286 // html : cells.length > 1 ? '' : ev.time
16290 cls: 'fc-event-title',
16291 html : String.format('{0}', ev.title)
16298 cls: 'ui-resizable-handle ui-resizable-e',
16299 html : '  '
16306 cfg.cls += ' fc-event-start';
16308 if ((i+1) == rows.length) {
16309 cfg.cls += ' fc-event-end';
16312 var ctr = _this.el.select('.fc-event-container',true).first();
16313 var cg = ctr.createChild(cfg);
16315 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16316 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16318 var r = (c.more.length) ? 1 : 0;
16319 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
16320 cg.setWidth(ebox.right - sbox.x -2);
16322 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16323 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16324 cg.on('click', _this.onEventClick, _this, ev);
16335 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16336 style : 'position: absolute',
16337 unselectable : "on",
16340 cls: 'fc-event-inner',
16344 cls: 'fc-event-title',
16352 cls: 'ui-resizable-handle ui-resizable-e',
16353 html : '  '
16359 var ctr = _this.el.select('.fc-event-container',true).first();
16360 var cg = ctr.createChild(cfg);
16362 var sbox = c.select('.fc-day-content',true).first().getBox();
16363 var ebox = c.select('.fc-day-content',true).first().getBox();
16365 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
16366 cg.setWidth(ebox.right - sbox.x -2);
16368 cg.on('click', _this.onMoreEventClick, _this, c.more);
16378 onEventEnter: function (e, el,event,d) {
16379 this.fireEvent('evententer', this, el, event);
16382 onEventLeave: function (e, el,event,d) {
16383 this.fireEvent('eventleave', this, el, event);
16386 onEventClick: function (e, el,event,d) {
16387 this.fireEvent('eventclick', this, el, event);
16390 onMonthChange: function () {
16394 onMoreEventClick: function(e, el, more)
16398 this.calpopover.placement = 'right';
16399 this.calpopover.setTitle('More');
16401 this.calpopover.setContent('');
16403 var ctr = this.calpopover.el.select('.popover-content', true).first();
16405 Roo.each(more, function(m){
16407 cls : 'fc-event-hori fc-event-draggable',
16410 var cg = ctr.createChild(cfg);
16412 cg.on('click', _this.onEventClick, _this, m);
16415 this.calpopover.show(el);
16420 onLoad: function ()
16422 this.calevents = [];
16425 if(this.store.getCount() > 0){
16426 this.store.data.each(function(d){
16429 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
16430 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
16431 time : d.data.start_time,
16432 title : d.data.title,
16433 description : d.data.description,
16434 venue : d.data.venue
16439 this.renderEvents();
16441 if(this.calevents.length && this.loadMask){
16442 this.maskEl.hide();
16446 onBeforeLoad: function()
16448 this.clearEvents();
16450 this.maskEl.show();
16464 * @class Roo.bootstrap.Popover
16465 * @extends Roo.bootstrap.Component
16466 * Bootstrap Popover class
16467 * @cfg {String} html contents of the popover (or false to use children..)
16468 * @cfg {String} title of popover (or false to hide)
16469 * @cfg {String} placement how it is placed
16470 * @cfg {String} trigger click || hover (or false to trigger manually)
16471 * @cfg {String} over what (parent or false to trigger manually.)
16472 * @cfg {Number} delay - delay before showing
16475 * Create a new Popover
16476 * @param {Object} config The config object
16479 Roo.bootstrap.Popover = function(config){
16480 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
16486 * After the popover show
16488 * @param {Roo.bootstrap.Popover} this
16493 * After the popover hide
16495 * @param {Roo.bootstrap.Popover} this
16501 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
16503 title: 'Fill in a title',
16506 placement : 'right',
16507 trigger : 'hover', // hover
16513 can_build_overlaid : false,
16515 getChildContainer : function()
16517 return this.el.select('.popover-content',true).first();
16520 getAutoCreate : function(){
16523 cls : 'popover roo-dynamic',
16524 style: 'display:block',
16530 cls : 'popover-inner',
16534 cls: 'popover-title',
16538 cls : 'popover-content',
16549 setTitle: function(str)
16552 this.el.select('.popover-title',true).first().dom.innerHTML = str;
16554 setContent: function(str)
16557 this.el.select('.popover-content',true).first().dom.innerHTML = str;
16559 // as it get's added to the bottom of the page.
16560 onRender : function(ct, position)
16562 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
16564 var cfg = Roo.apply({}, this.getAutoCreate());
16568 cfg.cls += ' ' + this.cls;
16571 cfg.style = this.style;
16573 //Roo.log("adding to ");
16574 this.el = Roo.get(document.body).createChild(cfg, position);
16575 // Roo.log(this.el);
16580 initEvents : function()
16582 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
16583 this.el.enableDisplayMode('block');
16585 if (this.over === false) {
16588 if (this.triggers === false) {
16591 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16592 var triggers = this.trigger ? this.trigger.split(' ') : [];
16593 Roo.each(triggers, function(trigger) {
16595 if (trigger == 'click') {
16596 on_el.on('click', this.toggle, this);
16597 } else if (trigger != 'manual') {
16598 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
16599 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
16601 on_el.on(eventIn ,this.enter, this);
16602 on_el.on(eventOut, this.leave, this);
16613 toggle : function () {
16614 this.hoverState == 'in' ? this.leave() : this.enter();
16617 enter : function () {
16619 clearTimeout(this.timeout);
16621 this.hoverState = 'in';
16623 if (!this.delay || !this.delay.show) {
16628 this.timeout = setTimeout(function () {
16629 if (_t.hoverState == 'in') {
16632 }, this.delay.show)
16635 leave : function() {
16636 clearTimeout(this.timeout);
16638 this.hoverState = 'out';
16640 if (!this.delay || !this.delay.hide) {
16645 this.timeout = setTimeout(function () {
16646 if (_t.hoverState == 'out') {
16649 }, this.delay.hide)
16652 show : function (on_el)
16655 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16659 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
16660 if (this.html !== false) {
16661 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
16663 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
16664 if (!this.title.length) {
16665 this.el.select('.popover-title',true).hide();
16668 var placement = typeof this.placement == 'function' ?
16669 this.placement.call(this, this.el, on_el) :
16672 var autoToken = /\s?auto?\s?/i;
16673 var autoPlace = autoToken.test(placement);
16675 placement = placement.replace(autoToken, '') || 'top';
16679 //this.el.setXY([0,0]);
16681 this.el.dom.style.display='block';
16682 this.el.addClass(placement);
16684 //this.el.appendTo(on_el);
16686 var p = this.getPosition();
16687 var box = this.el.getBox();
16692 var align = Roo.bootstrap.Popover.alignment[placement];
16693 this.el.alignTo(on_el, align[0],align[1]);
16694 //var arrow = this.el.select('.arrow',true).first();
16695 //arrow.set(align[2],
16697 this.el.addClass('in');
16700 if (this.el.hasClass('fade')) {
16704 this.hoverState = 'in';
16706 this.fireEvent('show', this);
16711 this.el.setXY([0,0]);
16712 this.el.removeClass('in');
16714 this.hoverState = null;
16716 this.fireEvent('hide', this);
16721 Roo.bootstrap.Popover.alignment = {
16722 'left' : ['r-l', [-10,0], 'right'],
16723 'right' : ['l-r', [10,0], 'left'],
16724 'bottom' : ['t-b', [0,10], 'top'],
16725 'top' : [ 'b-t', [0,-10], 'bottom']
16736 * @class Roo.bootstrap.Progress
16737 * @extends Roo.bootstrap.Component
16738 * Bootstrap Progress class
16739 * @cfg {Boolean} striped striped of the progress bar
16740 * @cfg {Boolean} active animated of the progress bar
16744 * Create a new Progress
16745 * @param {Object} config The config object
16748 Roo.bootstrap.Progress = function(config){
16749 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
16752 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
16757 getAutoCreate : function(){
16765 cfg.cls += ' progress-striped';
16769 cfg.cls += ' active';
16788 * @class Roo.bootstrap.ProgressBar
16789 * @extends Roo.bootstrap.Component
16790 * Bootstrap ProgressBar class
16791 * @cfg {Number} aria_valuenow aria-value now
16792 * @cfg {Number} aria_valuemin aria-value min
16793 * @cfg {Number} aria_valuemax aria-value max
16794 * @cfg {String} label label for the progress bar
16795 * @cfg {String} panel (success | info | warning | danger )
16796 * @cfg {String} role role of the progress bar
16797 * @cfg {String} sr_only text
16801 * Create a new ProgressBar
16802 * @param {Object} config The config object
16805 Roo.bootstrap.ProgressBar = function(config){
16806 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
16809 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
16813 aria_valuemax : 100,
16819 getAutoCreate : function()
16824 cls: 'progress-bar',
16825 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
16837 cfg.role = this.role;
16840 if(this.aria_valuenow){
16841 cfg['aria-valuenow'] = this.aria_valuenow;
16844 if(this.aria_valuemin){
16845 cfg['aria-valuemin'] = this.aria_valuemin;
16848 if(this.aria_valuemax){
16849 cfg['aria-valuemax'] = this.aria_valuemax;
16852 if(this.label && !this.sr_only){
16853 cfg.html = this.label;
16857 cfg.cls += ' progress-bar-' + this.panel;
16863 update : function(aria_valuenow)
16865 this.aria_valuenow = aria_valuenow;
16867 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
16882 * @class Roo.bootstrap.TabGroup
16883 * @extends Roo.bootstrap.Column
16884 * Bootstrap Column class
16885 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
16886 * @cfg {Boolean} carousel true to make the group behave like a carousel
16887 * @cfg {Boolean} bullets show bullets for the panels
16888 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
16889 * @cfg {Number} timer auto slide timer .. default 0 millisecond
16890 * @cfg {Boolean} showarrow (true|false) show arrow default true
16893 * Create a new TabGroup
16894 * @param {Object} config The config object
16897 Roo.bootstrap.TabGroup = function(config){
16898 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16900 this.navId = Roo.id();
16903 Roo.bootstrap.TabGroup.register(this);
16907 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
16910 transition : false,
16915 slideOnTouch : false,
16918 getAutoCreate : function()
16920 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16922 cfg.cls += ' tab-content';
16924 if (this.carousel) {
16925 cfg.cls += ' carousel slide';
16928 cls : 'carousel-inner',
16932 if(this.bullets && !Roo.isTouch){
16935 cls : 'carousel-bullets',
16939 if(this.bullets_cls){
16940 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
16947 cfg.cn[0].cn.push(bullets);
16950 if(this.showarrow){
16951 cfg.cn[0].cn.push({
16953 class : 'carousel-arrow',
16957 class : 'carousel-prev',
16961 class : 'fa fa-chevron-left'
16967 class : 'carousel-next',
16971 class : 'fa fa-chevron-right'
16984 initEvents: function()
16986 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
16987 // this.el.on("touchstart", this.onTouchStart, this);
16990 if(this.autoslide){
16993 this.slideFn = window.setInterval(function() {
16994 _this.showPanelNext();
16998 if(this.showarrow){
16999 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17000 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17006 // onTouchStart : function(e, el, o)
17008 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17012 // this.showPanelNext();
17016 getChildContainer : function()
17018 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17022 * register a Navigation item
17023 * @param {Roo.bootstrap.NavItem} the navitem to add
17025 register : function(item)
17027 this.tabs.push( item);
17028 item.navId = this.navId; // not really needed..
17033 getActivePanel : function()
17036 Roo.each(this.tabs, function(t) {
17046 getPanelByName : function(n)
17049 Roo.each(this.tabs, function(t) {
17050 if (t.tabId == n) {
17058 indexOfPanel : function(p)
17061 Roo.each(this.tabs, function(t,i) {
17062 if (t.tabId == p.tabId) {
17071 * show a specific panel
17072 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17073 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17075 showPanel : function (pan)
17077 if(this.transition || typeof(pan) == 'undefined'){
17078 Roo.log("waiting for the transitionend");
17082 if (typeof(pan) == 'number') {
17083 pan = this.tabs[pan];
17086 if (typeof(pan) == 'string') {
17087 pan = this.getPanelByName(pan);
17090 var cur = this.getActivePanel();
17093 Roo.log('pan or acitve pan is undefined');
17097 if (pan.tabId == this.getActivePanel().tabId) {
17101 if (false === cur.fireEvent('beforedeactivate')) {
17105 if(this.bullets > 0 && !Roo.isTouch){
17106 this.setActiveBullet(this.indexOfPanel(pan));
17109 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17111 this.transition = true;
17112 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
17113 var lr = dir == 'next' ? 'left' : 'right';
17114 pan.el.addClass(dir); // or prev
17115 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17116 cur.el.addClass(lr); // or right
17117 pan.el.addClass(lr);
17120 cur.el.on('transitionend', function() {
17121 Roo.log("trans end?");
17123 pan.el.removeClass([lr,dir]);
17124 pan.setActive(true);
17126 cur.el.removeClass([lr]);
17127 cur.setActive(false);
17129 _this.transition = false;
17131 }, this, { single: true } );
17136 cur.setActive(false);
17137 pan.setActive(true);
17142 showPanelNext : function()
17144 var i = this.indexOfPanel(this.getActivePanel());
17146 if (i >= this.tabs.length - 1 && !this.autoslide) {
17150 if (i >= this.tabs.length - 1 && this.autoslide) {
17154 this.showPanel(this.tabs[i+1]);
17157 showPanelPrev : function()
17159 var i = this.indexOfPanel(this.getActivePanel());
17161 if (i < 1 && !this.autoslide) {
17165 if (i < 1 && this.autoslide) {
17166 i = this.tabs.length;
17169 this.showPanel(this.tabs[i-1]);
17173 addBullet: function()
17175 if(!this.bullets || Roo.isTouch){
17178 var ctr = this.el.select('.carousel-bullets',true).first();
17179 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
17180 var bullet = ctr.createChild({
17181 cls : 'bullet bullet-' + i
17182 },ctr.dom.lastChild);
17187 bullet.on('click', (function(e, el, o, ii, t){
17189 e.preventDefault();
17191 this.showPanel(ii);
17193 if(this.autoslide && this.slideFn){
17194 clearInterval(this.slideFn);
17195 this.slideFn = window.setInterval(function() {
17196 _this.showPanelNext();
17200 }).createDelegate(this, [i, bullet], true));
17205 setActiveBullet : function(i)
17211 Roo.each(this.el.select('.bullet', true).elements, function(el){
17212 el.removeClass('selected');
17215 var bullet = this.el.select('.bullet-' + i, true).first();
17221 bullet.addClass('selected');
17232 Roo.apply(Roo.bootstrap.TabGroup, {
17236 * register a Navigation Group
17237 * @param {Roo.bootstrap.NavGroup} the navgroup to add
17239 register : function(navgrp)
17241 this.groups[navgrp.navId] = navgrp;
17245 * fetch a Navigation Group based on the navigation ID
17246 * if one does not exist , it will get created.
17247 * @param {string} the navgroup to add
17248 * @returns {Roo.bootstrap.NavGroup} the navgroup
17250 get: function(navId) {
17251 if (typeof(this.groups[navId]) == 'undefined') {
17252 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17254 return this.groups[navId] ;
17269 * @class Roo.bootstrap.TabPanel
17270 * @extends Roo.bootstrap.Component
17271 * Bootstrap TabPanel class
17272 * @cfg {Boolean} active panel active
17273 * @cfg {String} html panel content
17274 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17275 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17276 * @cfg {String} href click to link..
17280 * Create a new TabPanel
17281 * @param {Object} config The config object
17284 Roo.bootstrap.TabPanel = function(config){
17285 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17289 * Fires when the active status changes
17290 * @param {Roo.bootstrap.TabPanel} this
17291 * @param {Boolean} state the new state
17296 * @event beforedeactivate
17297 * Fires before a tab is de-activated - can be used to do validation on a form.
17298 * @param {Roo.bootstrap.TabPanel} this
17299 * @return {Boolean} false if there is an error
17302 'beforedeactivate': true
17305 this.tabId = this.tabId || Roo.id();
17309 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
17317 getAutoCreate : function(){
17320 // item is needed for carousel - not sure if it has any effect otherwise
17321 cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17322 html: this.html || ''
17326 cfg.cls += ' active';
17330 cfg.tabId = this.tabId;
17337 initEvents: function()
17339 var p = this.parent();
17341 this.navId = this.navId || p.navId;
17343 if (typeof(this.navId) != 'undefined') {
17344 // not really needed.. but just in case.. parent should be a NavGroup.
17345 var tg = Roo.bootstrap.TabGroup.get(this.navId);
17349 var i = tg.tabs.length - 1;
17351 if(this.active && tg.bullets > 0 && i < tg.bullets){
17352 tg.setActiveBullet(i);
17356 this.el.on('click', this.onClick, this);
17359 this.el.on("touchstart", this.onTouchStart, this);
17360 this.el.on("touchmove", this.onTouchMove, this);
17361 this.el.on("touchend", this.onTouchEnd, this);
17366 onRender : function(ct, position)
17368 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
17371 setActive : function(state)
17373 Roo.log("panel - set active " + this.tabId + "=" + state);
17375 this.active = state;
17377 this.el.removeClass('active');
17379 } else if (!this.el.hasClass('active')) {
17380 this.el.addClass('active');
17383 this.fireEvent('changed', this, state);
17386 onClick : function(e)
17388 e.preventDefault();
17390 if(!this.href.length){
17394 window.location.href = this.href;
17403 onTouchStart : function(e)
17405 this.swiping = false;
17407 this.startX = e.browserEvent.touches[0].clientX;
17408 this.startY = e.browserEvent.touches[0].clientY;
17411 onTouchMove : function(e)
17413 this.swiping = true;
17415 this.endX = e.browserEvent.touches[0].clientX;
17416 this.endY = e.browserEvent.touches[0].clientY;
17419 onTouchEnd : function(e)
17426 var tabGroup = this.parent();
17428 if(this.endX > this.startX){ // swiping right
17429 tabGroup.showPanelPrev();
17433 if(this.startX > this.endX){ // swiping left
17434 tabGroup.showPanelNext();
17453 * @class Roo.bootstrap.DateField
17454 * @extends Roo.bootstrap.Input
17455 * Bootstrap DateField class
17456 * @cfg {Number} weekStart default 0
17457 * @cfg {String} viewMode default empty, (months|years)
17458 * @cfg {String} minViewMode default empty, (months|years)
17459 * @cfg {Number} startDate default -Infinity
17460 * @cfg {Number} endDate default Infinity
17461 * @cfg {Boolean} todayHighlight default false
17462 * @cfg {Boolean} todayBtn default false
17463 * @cfg {Boolean} calendarWeeks default false
17464 * @cfg {Object} daysOfWeekDisabled default empty
17465 * @cfg {Boolean} singleMode default false (true | false)
17467 * @cfg {Boolean} keyboardNavigation default true
17468 * @cfg {String} language default en
17471 * Create a new DateField
17472 * @param {Object} config The config object
17475 Roo.bootstrap.DateField = function(config){
17476 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
17480 * Fires when this field show.
17481 * @param {Roo.bootstrap.DateField} this
17482 * @param {Mixed} date The date value
17487 * Fires when this field hide.
17488 * @param {Roo.bootstrap.DateField} this
17489 * @param {Mixed} date The date value
17494 * Fires when select a date.
17495 * @param {Roo.bootstrap.DateField} this
17496 * @param {Mixed} date The date value
17500 * @event beforeselect
17501 * Fires when before select a date.
17502 * @param {Roo.bootstrap.DateField} this
17503 * @param {Mixed} date The date value
17505 beforeselect : true
17509 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
17512 * @cfg {String} format
17513 * The default date format string which can be overriden for localization support. The format must be
17514 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17518 * @cfg {String} altFormats
17519 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17520 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17522 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17530 todayHighlight : false,
17536 keyboardNavigation: true,
17538 calendarWeeks: false,
17540 startDate: -Infinity,
17544 daysOfWeekDisabled: [],
17548 singleMode : false,
17550 UTCDate: function()
17552 return new Date(Date.UTC.apply(Date, arguments));
17555 UTCToday: function()
17557 var today = new Date();
17558 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
17561 getDate: function() {
17562 var d = this.getUTCDate();
17563 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
17566 getUTCDate: function() {
17570 setDate: function(d) {
17571 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
17574 setUTCDate: function(d) {
17576 this.setValue(this.formatDate(this.date));
17579 onRender: function(ct, position)
17582 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
17584 this.language = this.language || 'en';
17585 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
17586 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
17588 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
17589 this.format = this.format || 'm/d/y';
17590 this.isInline = false;
17591 this.isInput = true;
17592 this.component = this.el.select('.add-on', true).first() || false;
17593 this.component = (this.component && this.component.length === 0) ? false : this.component;
17594 this.hasInput = this.component && this.inputEl().length;
17596 if (typeof(this.minViewMode === 'string')) {
17597 switch (this.minViewMode) {
17599 this.minViewMode = 1;
17602 this.minViewMode = 2;
17605 this.minViewMode = 0;
17610 if (typeof(this.viewMode === 'string')) {
17611 switch (this.viewMode) {
17624 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
17626 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
17628 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17630 this.picker().on('mousedown', this.onMousedown, this);
17631 this.picker().on('click', this.onClick, this);
17633 this.picker().addClass('datepicker-dropdown');
17635 this.startViewMode = this.viewMode;
17637 if(this.singleMode){
17638 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
17639 v.setVisibilityMode(Roo.Element.DISPLAY);
17643 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17644 v.setStyle('width', '189px');
17648 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
17649 if(!this.calendarWeeks){
17654 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17655 v.attr('colspan', function(i, val){
17656 return parseInt(val) + 1;
17661 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
17663 this.setStartDate(this.startDate);
17664 this.setEndDate(this.endDate);
17666 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
17673 if(this.isInline) {
17678 picker : function()
17680 return this.pickerEl;
17681 // return this.el.select('.datepicker', true).first();
17684 fillDow: function()
17686 var dowCnt = this.weekStart;
17695 if(this.calendarWeeks){
17703 while (dowCnt < this.weekStart + 7) {
17707 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
17711 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
17714 fillMonths: function()
17717 var months = this.picker().select('>.datepicker-months td', true).first();
17719 months.dom.innerHTML = '';
17725 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
17728 months.createChild(month);
17735 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;
17737 if (this.date < this.startDate) {
17738 this.viewDate = new Date(this.startDate);
17739 } else if (this.date > this.endDate) {
17740 this.viewDate = new Date(this.endDate);
17742 this.viewDate = new Date(this.date);
17750 var d = new Date(this.viewDate),
17751 year = d.getUTCFullYear(),
17752 month = d.getUTCMonth(),
17753 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
17754 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
17755 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
17756 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
17757 currentDate = this.date && this.date.valueOf(),
17758 today = this.UTCToday();
17760 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
17762 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17764 // this.picker.select('>tfoot th.today').
17765 // .text(dates[this.language].today)
17766 // .toggle(this.todayBtn !== false);
17768 this.updateNavArrows();
17771 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
17773 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
17775 prevMonth.setUTCDate(day);
17777 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
17779 var nextMonth = new Date(prevMonth);
17781 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
17783 nextMonth = nextMonth.valueOf();
17785 var fillMonths = false;
17787 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
17789 while(prevMonth.valueOf() < nextMonth) {
17792 if (prevMonth.getUTCDay() === this.weekStart) {
17794 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
17802 if(this.calendarWeeks){
17803 // ISO 8601: First week contains first thursday.
17804 // ISO also states week starts on Monday, but we can be more abstract here.
17806 // Start of current week: based on weekstart/current date
17807 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
17808 // Thursday of this week
17809 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
17810 // First Thursday of year, year from thursday
17811 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
17812 // Calendar week: ms between thursdays, div ms per day, div 7 days
17813 calWeek = (th - yth) / 864e5 / 7 + 1;
17815 fillMonths.cn.push({
17823 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
17825 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
17828 if (this.todayHighlight &&
17829 prevMonth.getUTCFullYear() == today.getFullYear() &&
17830 prevMonth.getUTCMonth() == today.getMonth() &&
17831 prevMonth.getUTCDate() == today.getDate()) {
17832 clsName += ' today';
17835 if (currentDate && prevMonth.valueOf() === currentDate) {
17836 clsName += ' active';
17839 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
17840 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
17841 clsName += ' disabled';
17844 fillMonths.cn.push({
17846 cls: 'day ' + clsName,
17847 html: prevMonth.getDate()
17850 prevMonth.setDate(prevMonth.getDate()+1);
17853 var currentYear = this.date && this.date.getUTCFullYear();
17854 var currentMonth = this.date && this.date.getUTCMonth();
17856 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
17858 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
17859 v.removeClass('active');
17861 if(currentYear === year && k === currentMonth){
17862 v.addClass('active');
17865 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
17866 v.addClass('disabled');
17872 year = parseInt(year/10, 10) * 10;
17874 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
17876 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
17879 for (var i = -1; i < 11; i++) {
17880 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
17882 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
17890 showMode: function(dir)
17893 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
17896 Roo.each(this.picker().select('>div',true).elements, function(v){
17897 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17900 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
17905 if(this.isInline) {
17909 this.picker().removeClass(['bottom', 'top']);
17911 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17913 * place to the top of element!
17917 this.picker().addClass('top');
17918 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17923 this.picker().addClass('bottom');
17925 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17928 parseDate : function(value)
17930 if(!value || value instanceof Date){
17933 var v = Date.parseDate(value, this.format);
17934 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
17935 v = Date.parseDate(value, 'Y-m-d');
17937 if(!v && this.altFormats){
17938 if(!this.altFormatsArray){
17939 this.altFormatsArray = this.altFormats.split("|");
17941 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17942 v = Date.parseDate(value, this.altFormatsArray[i]);
17948 formatDate : function(date, fmt)
17950 return (!date || !(date instanceof Date)) ?
17951 date : date.dateFormat(fmt || this.format);
17954 onFocus : function()
17956 Roo.bootstrap.DateField.superclass.onFocus.call(this);
17960 onBlur : function()
17962 Roo.bootstrap.DateField.superclass.onBlur.call(this);
17964 var d = this.inputEl().getValue();
17973 this.picker().show();
17977 this.fireEvent('show', this, this.date);
17982 if(this.isInline) {
17985 this.picker().hide();
17986 this.viewMode = this.startViewMode;
17989 this.fireEvent('hide', this, this.date);
17993 onMousedown: function(e)
17995 e.stopPropagation();
17996 e.preventDefault();
18001 Roo.bootstrap.DateField.superclass.keyup.call(this);
18005 setValue: function(v)
18007 if(this.fireEvent('beforeselect', this, v) !== false){
18008 var d = new Date(this.parseDate(v) ).clearTime();
18010 if(isNaN(d.getTime())){
18011 this.date = this.viewDate = '';
18012 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18016 v = this.formatDate(d);
18018 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18020 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18024 this.fireEvent('select', this, this.date);
18028 getValue: function()
18030 return this.formatDate(this.date);
18033 fireKey: function(e)
18035 if (!this.picker().isVisible()){
18036 if (e.keyCode == 27) { // allow escape to hide and re-show picker
18042 var dateChanged = false,
18044 newDate, newViewDate;
18049 e.preventDefault();
18053 if (!this.keyboardNavigation) {
18056 dir = e.keyCode == 37 ? -1 : 1;
18059 newDate = this.moveYear(this.date, dir);
18060 newViewDate = this.moveYear(this.viewDate, dir);
18061 } else if (e.shiftKey){
18062 newDate = this.moveMonth(this.date, dir);
18063 newViewDate = this.moveMonth(this.viewDate, dir);
18065 newDate = new Date(this.date);
18066 newDate.setUTCDate(this.date.getUTCDate() + dir);
18067 newViewDate = new Date(this.viewDate);
18068 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18070 if (this.dateWithinRange(newDate)){
18071 this.date = newDate;
18072 this.viewDate = newViewDate;
18073 this.setValue(this.formatDate(this.date));
18075 e.preventDefault();
18076 dateChanged = true;
18081 if (!this.keyboardNavigation) {
18084 dir = e.keyCode == 38 ? -1 : 1;
18086 newDate = this.moveYear(this.date, dir);
18087 newViewDate = this.moveYear(this.viewDate, dir);
18088 } else if (e.shiftKey){
18089 newDate = this.moveMonth(this.date, dir);
18090 newViewDate = this.moveMonth(this.viewDate, dir);
18092 newDate = new Date(this.date);
18093 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18094 newViewDate = new Date(this.viewDate);
18095 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18097 if (this.dateWithinRange(newDate)){
18098 this.date = newDate;
18099 this.viewDate = newViewDate;
18100 this.setValue(this.formatDate(this.date));
18102 e.preventDefault();
18103 dateChanged = true;
18107 this.setValue(this.formatDate(this.date));
18109 e.preventDefault();
18112 this.setValue(this.formatDate(this.date));
18126 onClick: function(e)
18128 e.stopPropagation();
18129 e.preventDefault();
18131 var target = e.getTarget();
18133 if(target.nodeName.toLowerCase() === 'i'){
18134 target = Roo.get(target).dom.parentNode;
18137 var nodeName = target.nodeName;
18138 var className = target.className;
18139 var html = target.innerHTML;
18140 //Roo.log(nodeName);
18142 switch(nodeName.toLowerCase()) {
18144 switch(className) {
18150 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18151 switch(this.viewMode){
18153 this.viewDate = this.moveMonth(this.viewDate, dir);
18157 this.viewDate = this.moveYear(this.viewDate, dir);
18163 var date = new Date();
18164 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18166 this.setValue(this.formatDate(this.date));
18173 if (className.indexOf('disabled') < 0) {
18174 this.viewDate.setUTCDate(1);
18175 if (className.indexOf('month') > -1) {
18176 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18178 var year = parseInt(html, 10) || 0;
18179 this.viewDate.setUTCFullYear(year);
18183 if(this.singleMode){
18184 this.setValue(this.formatDate(this.viewDate));
18195 //Roo.log(className);
18196 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
18197 var day = parseInt(html, 10) || 1;
18198 var year = this.viewDate.getUTCFullYear(),
18199 month = this.viewDate.getUTCMonth();
18201 if (className.indexOf('old') > -1) {
18208 } else if (className.indexOf('new') > -1) {
18216 //Roo.log([year,month,day]);
18217 this.date = this.UTCDate(year, month, day,0,0,0,0);
18218 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
18220 //Roo.log(this.formatDate(this.date));
18221 this.setValue(this.formatDate(this.date));
18228 setStartDate: function(startDate)
18230 this.startDate = startDate || -Infinity;
18231 if (this.startDate !== -Infinity) {
18232 this.startDate = this.parseDate(this.startDate);
18235 this.updateNavArrows();
18238 setEndDate: function(endDate)
18240 this.endDate = endDate || Infinity;
18241 if (this.endDate !== Infinity) {
18242 this.endDate = this.parseDate(this.endDate);
18245 this.updateNavArrows();
18248 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18250 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18251 if (typeof(this.daysOfWeekDisabled) !== 'object') {
18252 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18254 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18255 return parseInt(d, 10);
18258 this.updateNavArrows();
18261 updateNavArrows: function()
18263 if(this.singleMode){
18267 var d = new Date(this.viewDate),
18268 year = d.getUTCFullYear(),
18269 month = d.getUTCMonth();
18271 Roo.each(this.picker().select('.prev', true).elements, function(v){
18273 switch (this.viewMode) {
18276 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18282 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18289 Roo.each(this.picker().select('.next', true).elements, function(v){
18291 switch (this.viewMode) {
18294 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18300 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18308 moveMonth: function(date, dir)
18313 var new_date = new Date(date.valueOf()),
18314 day = new_date.getUTCDate(),
18315 month = new_date.getUTCMonth(),
18316 mag = Math.abs(dir),
18318 dir = dir > 0 ? 1 : -1;
18321 // If going back one month, make sure month is not current month
18322 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18324 return new_date.getUTCMonth() == month;
18326 // If going forward one month, make sure month is as expected
18327 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18329 return new_date.getUTCMonth() != new_month;
18331 new_month = month + dir;
18332 new_date.setUTCMonth(new_month);
18333 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18334 if (new_month < 0 || new_month > 11) {
18335 new_month = (new_month + 12) % 12;
18338 // For magnitudes >1, move one month at a time...
18339 for (var i=0; i<mag; i++) {
18340 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18341 new_date = this.moveMonth(new_date, dir);
18343 // ...then reset the day, keeping it in the new month
18344 new_month = new_date.getUTCMonth();
18345 new_date.setUTCDate(day);
18347 return new_month != new_date.getUTCMonth();
18350 // Common date-resetting loop -- if date is beyond end of month, make it
18353 new_date.setUTCDate(--day);
18354 new_date.setUTCMonth(new_month);
18359 moveYear: function(date, dir)
18361 return this.moveMonth(date, dir*12);
18364 dateWithinRange: function(date)
18366 return date >= this.startDate && date <= this.endDate;
18372 this.picker().remove();
18375 validateValue : function(value)
18377 if(value.length < 1) {
18378 if(this.allowBlank){
18384 if(value.length < this.minLength){
18387 if(value.length > this.maxLength){
18391 var vt = Roo.form.VTypes;
18392 if(!vt[this.vtype](value, this)){
18396 if(typeof this.validator == "function"){
18397 var msg = this.validator(value);
18403 if(this.regex && !this.regex.test(value)){
18407 if(typeof(this.parseDate(value)) == 'undefined'){
18411 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
18415 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
18425 Roo.apply(Roo.bootstrap.DateField, {
18436 html: '<i class="fa fa-arrow-left"/>'
18446 html: '<i class="fa fa-arrow-right"/>'
18488 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
18489 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
18490 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
18491 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18492 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
18505 navFnc: 'FullYear',
18510 navFnc: 'FullYear',
18515 Roo.apply(Roo.bootstrap.DateField, {
18519 cls: 'datepicker dropdown-menu roo-dynamic',
18523 cls: 'datepicker-days',
18527 cls: 'table-condensed',
18529 Roo.bootstrap.DateField.head,
18533 Roo.bootstrap.DateField.footer
18540 cls: 'datepicker-months',
18544 cls: 'table-condensed',
18546 Roo.bootstrap.DateField.head,
18547 Roo.bootstrap.DateField.content,
18548 Roo.bootstrap.DateField.footer
18555 cls: 'datepicker-years',
18559 cls: 'table-condensed',
18561 Roo.bootstrap.DateField.head,
18562 Roo.bootstrap.DateField.content,
18563 Roo.bootstrap.DateField.footer
18582 * @class Roo.bootstrap.TimeField
18583 * @extends Roo.bootstrap.Input
18584 * Bootstrap DateField class
18588 * Create a new TimeField
18589 * @param {Object} config The config object
18592 Roo.bootstrap.TimeField = function(config){
18593 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
18597 * Fires when this field show.
18598 * @param {Roo.bootstrap.DateField} thisthis
18599 * @param {Mixed} date The date value
18604 * Fires when this field hide.
18605 * @param {Roo.bootstrap.DateField} this
18606 * @param {Mixed} date The date value
18611 * Fires when select a date.
18612 * @param {Roo.bootstrap.DateField} this
18613 * @param {Mixed} date The date value
18619 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
18622 * @cfg {String} format
18623 * The default time format string which can be overriden for localization support. The format must be
18624 * valid according to {@link Date#parseDate} (defaults to 'H:i').
18628 onRender: function(ct, position)
18631 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
18633 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
18635 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18637 this.pop = this.picker().select('>.datepicker-time',true).first();
18638 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18640 this.picker().on('mousedown', this.onMousedown, this);
18641 this.picker().on('click', this.onClick, this);
18643 this.picker().addClass('datepicker-dropdown');
18648 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
18649 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
18650 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
18651 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
18652 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
18653 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
18657 fireKey: function(e){
18658 if (!this.picker().isVisible()){
18659 if (e.keyCode == 27) { // allow escape to hide and re-show picker
18665 e.preventDefault();
18673 this.onTogglePeriod();
18676 this.onIncrementMinutes();
18679 this.onDecrementMinutes();
18688 onClick: function(e) {
18689 e.stopPropagation();
18690 e.preventDefault();
18693 picker : function()
18695 return this.el.select('.datepicker', true).first();
18698 fillTime: function()
18700 var time = this.pop.select('tbody', true).first();
18702 time.dom.innerHTML = '';
18717 cls: 'hours-up glyphicon glyphicon-chevron-up'
18737 cls: 'minutes-up glyphicon glyphicon-chevron-up'
18758 cls: 'timepicker-hour',
18773 cls: 'timepicker-minute',
18788 cls: 'btn btn-primary period',
18810 cls: 'hours-down glyphicon glyphicon-chevron-down'
18830 cls: 'minutes-down glyphicon glyphicon-chevron-down'
18848 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
18855 var hours = this.time.getHours();
18856 var minutes = this.time.getMinutes();
18869 hours = hours - 12;
18873 hours = '0' + hours;
18877 minutes = '0' + minutes;
18880 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
18881 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
18882 this.pop.select('button', true).first().dom.innerHTML = period;
18888 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
18890 var cls = ['bottom'];
18892 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
18899 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
18904 this.picker().addClass(cls.join('-'));
18908 Roo.each(cls, function(c){
18910 _this.picker().setTop(_this.inputEl().getHeight());
18914 _this.picker().setTop(0 - _this.picker().getHeight());
18919 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
18923 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
18930 onFocus : function()
18932 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
18936 onBlur : function()
18938 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
18944 this.picker().show();
18949 this.fireEvent('show', this, this.date);
18954 this.picker().hide();
18957 this.fireEvent('hide', this, this.date);
18960 setTime : function()
18963 this.setValue(this.time.format(this.format));
18965 this.fireEvent('select', this, this.date);
18970 onMousedown: function(e){
18971 e.stopPropagation();
18972 e.preventDefault();
18975 onIncrementHours: function()
18977 Roo.log('onIncrementHours');
18978 this.time = this.time.add(Date.HOUR, 1);
18983 onDecrementHours: function()
18985 Roo.log('onDecrementHours');
18986 this.time = this.time.add(Date.HOUR, -1);
18990 onIncrementMinutes: function()
18992 Roo.log('onIncrementMinutes');
18993 this.time = this.time.add(Date.MINUTE, 1);
18997 onDecrementMinutes: function()
18999 Roo.log('onDecrementMinutes');
19000 this.time = this.time.add(Date.MINUTE, -1);
19004 onTogglePeriod: function()
19006 Roo.log('onTogglePeriod');
19007 this.time = this.time.add(Date.HOUR, 12);
19014 Roo.apply(Roo.bootstrap.TimeField, {
19044 cls: 'btn btn-info ok',
19056 Roo.apply(Roo.bootstrap.TimeField, {
19060 cls: 'datepicker dropdown-menu',
19064 cls: 'datepicker-time',
19068 cls: 'table-condensed',
19070 Roo.bootstrap.TimeField.content,
19071 Roo.bootstrap.TimeField.footer
19090 * @class Roo.bootstrap.MonthField
19091 * @extends Roo.bootstrap.Input
19092 * Bootstrap MonthField class
19094 * @cfg {String} language default en
19097 * Create a new MonthField
19098 * @param {Object} config The config object
19101 Roo.bootstrap.MonthField = function(config){
19102 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19107 * Fires when this field show.
19108 * @param {Roo.bootstrap.MonthField} this
19109 * @param {Mixed} date The date value
19114 * Fires when this field hide.
19115 * @param {Roo.bootstrap.MonthField} this
19116 * @param {Mixed} date The date value
19121 * Fires when select a date.
19122 * @param {Roo.bootstrap.MonthField} this
19123 * @param {String} oldvalue The old value
19124 * @param {String} newvalue The new value
19130 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
19132 onRender: function(ct, position)
19135 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19137 this.language = this.language || 'en';
19138 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19139 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19141 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19142 this.isInline = false;
19143 this.isInput = true;
19144 this.component = this.el.select('.add-on', true).first() || false;
19145 this.component = (this.component && this.component.length === 0) ? false : this.component;
19146 this.hasInput = this.component && this.inputEL().length;
19148 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19150 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19152 this.picker().on('mousedown', this.onMousedown, this);
19153 this.picker().on('click', this.onClick, this);
19155 this.picker().addClass('datepicker-dropdown');
19157 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19158 v.setStyle('width', '189px');
19165 if(this.isInline) {
19171 setValue: function(v, suppressEvent)
19173 var o = this.getValue();
19175 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
19179 if(suppressEvent !== true){
19180 this.fireEvent('select', this, o, v);
19185 getValue: function()
19190 onClick: function(e)
19192 e.stopPropagation();
19193 e.preventDefault();
19195 var target = e.getTarget();
19197 if(target.nodeName.toLowerCase() === 'i'){
19198 target = Roo.get(target).dom.parentNode;
19201 var nodeName = target.nodeName;
19202 var className = target.className;
19203 var html = target.innerHTML;
19205 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
19209 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
19211 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19217 picker : function()
19219 return this.pickerEl;
19222 fillMonths: function()
19225 var months = this.picker().select('>.datepicker-months td', true).first();
19227 months.dom.innerHTML = '';
19233 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
19236 months.createChild(month);
19245 if(typeof(this.vIndex) == 'undefined' && this.value.length){
19246 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19249 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19250 e.removeClass('active');
19252 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19253 e.addClass('active');
19260 if(this.isInline) {
19264 this.picker().removeClass(['bottom', 'top']);
19266 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19268 * place to the top of element!
19272 this.picker().addClass('top');
19273 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19278 this.picker().addClass('bottom');
19280 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19283 onFocus : function()
19285 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19289 onBlur : function()
19291 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19293 var d = this.inputEl().getValue();
19302 this.picker().show();
19303 this.picker().select('>.datepicker-months', true).first().show();
19307 this.fireEvent('show', this, this.date);
19312 if(this.isInline) {
19315 this.picker().hide();
19316 this.fireEvent('hide', this, this.date);
19320 onMousedown: function(e)
19322 e.stopPropagation();
19323 e.preventDefault();
19328 Roo.bootstrap.MonthField.superclass.keyup.call(this);
19332 fireKey: function(e)
19334 if (!this.picker().isVisible()){
19335 if (e.keyCode == 27) {// allow escape to hide and re-show picker
19346 e.preventDefault();
19350 dir = e.keyCode == 37 ? -1 : 1;
19352 this.vIndex = this.vIndex + dir;
19354 if(this.vIndex < 0){
19358 if(this.vIndex > 11){
19362 if(isNaN(this.vIndex)){
19366 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19372 dir = e.keyCode == 38 ? -1 : 1;
19374 this.vIndex = this.vIndex + dir * 4;
19376 if(this.vIndex < 0){
19380 if(this.vIndex > 11){
19384 if(isNaN(this.vIndex)){
19388 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19393 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19394 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19398 e.preventDefault();
19401 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19402 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19418 this.picker().remove();
19423 Roo.apply(Roo.bootstrap.MonthField, {
19442 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19443 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
19448 Roo.apply(Roo.bootstrap.MonthField, {
19452 cls: 'datepicker dropdown-menu roo-dynamic',
19456 cls: 'datepicker-months',
19460 cls: 'table-condensed',
19462 Roo.bootstrap.DateField.content
19482 * @class Roo.bootstrap.CheckBox
19483 * @extends Roo.bootstrap.Input
19484 * Bootstrap CheckBox class
19486 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
19487 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
19488 * @cfg {String} boxLabel The text that appears beside the checkbox
19489 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
19490 * @cfg {Boolean} checked initnal the element
19491 * @cfg {Boolean} inline inline the element (default false)
19492 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
19495 * Create a new CheckBox
19496 * @param {Object} config The config object
19499 Roo.bootstrap.CheckBox = function(config){
19500 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
19505 * Fires when the element is checked or unchecked.
19506 * @param {Roo.bootstrap.CheckBox} this This input
19507 * @param {Boolean} checked The new checked value
19514 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
19516 inputType: 'checkbox',
19524 getAutoCreate : function()
19526 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19532 cfg.cls = 'form-group ' + this.inputType; //input-group
19535 cfg.cls += ' ' + this.inputType + '-inline';
19541 type : this.inputType,
19542 value : this.inputValue,
19543 cls : 'roo-' + this.inputType, //'form-box',
19544 placeholder : this.placeholder || ''
19548 if(this.inputType != 'radio'){
19552 cls : 'roo-hidden-value',
19553 value : this.checked ? this.valueOff : this.inputValue
19558 if (this.weight) { // Validity check?
19559 cfg.cls += " " + this.inputType + "-" + this.weight;
19562 if (this.disabled) {
19563 input.disabled=true;
19567 input.checked = this.checked;
19574 input.name = this.name;
19576 if(this.inputType != 'radio'){
19577 hidden.name = this.name;
19578 input.name = '_hidden_' + this.name;
19583 input.cls += ' input-' + this.size;
19588 ['xs','sm','md','lg'].map(function(size){
19589 if (settings[size]) {
19590 cfg.cls += ' col-' + size + '-' + settings[size];
19594 var inputblock = input;
19596 if (this.before || this.after) {
19599 cls : 'input-group',
19604 inputblock.cn.push({
19606 cls : 'input-group-addon',
19611 inputblock.cn.push(input);
19613 if(this.inputType != 'radio'){
19614 inputblock.cn.push(hidden);
19618 inputblock.cn.push({
19620 cls : 'input-group-addon',
19627 if (align ==='left' && this.fieldLabel.length) {
19628 // Roo.log("left and has label");
19634 cls : 'control-label col-md-' + this.labelWidth,
19635 html : this.fieldLabel
19639 cls : "col-md-" + (12 - this.labelWidth),
19646 } else if ( this.fieldLabel.length) {
19647 // Roo.log(" label");
19651 tag: this.boxLabel ? 'span' : 'label',
19653 cls: 'control-label box-input-label',
19654 //cls : 'input-group-addon',
19655 html : this.fieldLabel
19665 // Roo.log(" no label && no align");
19666 cfg.cn = [ inputblock ] ;
19672 var boxLabelCfg = {
19674 //'for': id, // box label is handled by onclick - so no for...
19676 html: this.boxLabel
19680 boxLabelCfg.tooltip = this.tooltip;
19683 cfg.cn.push(boxLabelCfg);
19686 if(this.inputType != 'radio'){
19687 cfg.cn.push(hidden);
19695 * return the real input element.
19697 inputEl: function ()
19699 return this.el.select('input.roo-' + this.inputType,true).first();
19701 hiddenEl: function ()
19703 return this.el.select('input.roo-hidden-value',true).first();
19706 labelEl: function()
19708 return this.el.select('label.control-label',true).first();
19710 /* depricated... */
19714 return this.labelEl();
19717 boxLabelEl: function()
19719 return this.el.select('label.box-label',true).first();
19722 initEvents : function()
19724 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19726 this.inputEl().on('click', this.onClick, this);
19728 if (this.boxLabel) {
19729 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
19732 this.startValue = this.getValue();
19735 Roo.bootstrap.CheckBox.register(this);
19739 onClick : function()
19741 this.setChecked(!this.checked);
19744 setChecked : function(state,suppressEvent)
19746 this.startValue = this.getValue();
19748 if(this.inputType == 'radio'){
19750 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19751 e.dom.checked = false;
19754 this.inputEl().dom.checked = true;
19756 this.inputEl().dom.value = this.inputValue;
19758 if(suppressEvent !== true){
19759 this.fireEvent('check', this, true);
19767 this.checked = state;
19769 this.inputEl().dom.checked = state;
19772 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
19774 if(suppressEvent !== true){
19775 this.fireEvent('check', this, state);
19781 getValue : function()
19783 if(this.inputType == 'radio'){
19784 return this.getGroupValue();
19787 return this.hiddenEl().dom.value;
19791 getGroupValue : function()
19793 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
19797 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
19800 setValue : function(v,suppressEvent)
19802 if(this.inputType == 'radio'){
19803 this.setGroupValue(v, suppressEvent);
19807 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
19812 setGroupValue : function(v, suppressEvent)
19814 this.startValue = this.getValue();
19816 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19817 e.dom.checked = false;
19819 if(e.dom.value == v){
19820 e.dom.checked = true;
19824 if(suppressEvent !== true){
19825 this.fireEvent('check', this, true);
19833 validate : function()
19837 (this.inputType == 'radio' && this.validateRadio()) ||
19838 (this.inputType == 'checkbox' && this.validateCheckbox())
19844 this.markInvalid();
19848 validateRadio : function()
19850 if(this.allowBlank){
19856 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19857 if(!e.dom.checked){
19869 validateCheckbox : function()
19872 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
19875 var group = Roo.bootstrap.CheckBox.get(this.groupId);
19883 for(var i in group){
19888 r = (group[i].getValue() == group[i].inputValue) ? true : false;
19895 * Mark this field as valid
19897 markValid : function()
19899 if(this.allowBlank){
19905 this.fireEvent('valid', this);
19907 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19910 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19917 if(this.inputType == 'radio'){
19918 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19919 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19920 e.findParent('.form-group', false, true).addClass(_this.validClass);
19927 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19928 this.el.findParent('.form-group', false, true).addClass(this.validClass);
19932 var group = Roo.bootstrap.CheckBox.get(this.groupId);
19938 for(var i in group){
19939 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19940 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
19945 * Mark this field as invalid
19946 * @param {String} msg The validation message
19948 markInvalid : function(msg)
19950 if(this.allowBlank){
19956 this.fireEvent('invalid', this, msg);
19958 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19961 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19965 label.markInvalid();
19968 if(this.inputType == 'radio'){
19969 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19970 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19971 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
19978 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19979 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
19983 var group = Roo.bootstrap.CheckBox.get(this.groupId);
19989 for(var i in group){
19990 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19991 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
19996 disable : function()
19998 if(this.inputType != 'radio'){
19999 Roo.bootstrap.CheckBox.superclass.disable.call(this);
20006 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20007 _this.getActionEl().addClass(this.disabledClass);
20008 e.dom.disabled = true;
20012 this.disabled = true;
20013 this.fireEvent("disable", this);
20017 enable : function()
20019 if(this.inputType != 'radio'){
20020 Roo.bootstrap.CheckBox.superclass.enable.call(this);
20027 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20028 _this.getActionEl().removeClass(this.disabledClass);
20029 e.dom.disabled = false;
20033 this.disabled = false;
20034 this.fireEvent("enable", this);
20040 Roo.apply(Roo.bootstrap.CheckBox, {
20045 * register a CheckBox Group
20046 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20048 register : function(checkbox)
20050 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20051 this.groups[checkbox.groupId] = {};
20054 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20058 this.groups[checkbox.groupId][checkbox.name] = checkbox;
20062 * fetch a CheckBox Group based on the group ID
20063 * @param {string} the group ID
20064 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20066 get: function(groupId) {
20067 if (typeof(this.groups[groupId]) == 'undefined') {
20071 return this.groups[groupId] ;
20083 *<div class="radio">
20085 <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
20086 Option one is this and that—be sure to include why it's great
20093 *<label class="radio-inline">fieldLabel</label>
20094 *<label class="radio-inline">
20095 <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
20103 * @class Roo.bootstrap.Radio
20104 * @extends Roo.bootstrap.CheckBox
20105 * Bootstrap Radio class
20108 * Create a new Radio
20109 * @param {Object} config The config object
20112 Roo.bootstrap.Radio = function(config){
20113 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
20117 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
20119 inputType: 'radio',
20123 getAutoCreate : function()
20125 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20126 align = align || 'left'; // default...
20133 tag : this.inline ? 'span' : 'div',
20134 cls : 'form-group',
20138 var inline = this.inline ? ' radio-inline' : '';
20142 // does not need for, as we wrap the input with it..
20144 cls : 'control-label box-label' + inline,
20147 var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
20151 //cls : 'control-label' + inline,
20152 html : this.fieldLabel,
20153 style : 'width:' + labelWidth + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
20159 type : this.inputType,
20160 //value : (!this.checked) ? this.valueOff : this.inputValue,
20161 value : this.inputValue,
20163 placeholder : this.placeholder || '' // ?? needed????
20166 if (this.weight) { // Validity check?
20167 input.cls += " radio-" + this.weight;
20169 if (this.disabled) {
20170 input.disabled=true;
20174 input.checked = this.checked;
20178 input.name = this.name;
20182 input.cls += ' input-' + this.size;
20185 //?? can span's inline have a width??
20188 ['xs','sm','md','lg'].map(function(size){
20189 if (settings[size]) {
20190 cfg.cls += ' col-' + size + '-' + settings[size];
20194 var inputblock = input;
20196 if (this.before || this.after) {
20199 cls : 'input-group',
20204 inputblock.cn.push({
20206 cls : 'input-group-addon',
20210 inputblock.cn.push(input);
20212 inputblock.cn.push({
20214 cls : 'input-group-addon',
20222 if (this.fieldLabel && this.fieldLabel.length) {
20223 cfg.cn.push(fieldLabel);
20226 // normal bootstrap puts the input inside the label.
20227 // however with our styled version - it has to go after the input.
20229 //lbl.cn.push(inputblock);
20233 cls: 'radio' + inline,
20240 cfg.cn.push( lblwrap);
20245 html: this.boxLabel
20254 initEvents : function()
20256 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20258 this.inputEl().on('click', this.onClick, this);
20259 if (this.boxLabel) {
20260 //Roo.log('find label');
20261 this.el.select('span.radio label span',true).first().on('click', this.onClick, this);
20266 inputEl: function ()
20268 return this.el.select('input.roo-radio',true).first();
20270 onClick : function()
20273 this.setChecked(true);
20276 setChecked : function(state,suppressEvent)
20279 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
20280 v.dom.checked = false;
20283 Roo.log(this.inputEl().dom);
20284 this.checked = state;
20285 this.inputEl().dom.checked = state;
20287 if(suppressEvent !== true){
20288 this.fireEvent('check', this, state);
20291 //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
20295 getGroupValue : function()
20298 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
20299 if(v.dom.checked == true){
20300 value = v.dom.value;
20308 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
20309 * @return {Mixed} value The field value
20311 getValue : function(){
20312 return this.getGroupValue();
20316 //<script type="text/javascript">
20319 * Based Ext JS Library 1.1.1
20320 * Copyright(c) 2006-2007, Ext JS, LLC.
20326 * @class Roo.HtmlEditorCore
20327 * @extends Roo.Component
20328 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20330 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20333 Roo.HtmlEditorCore = function(config){
20336 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20341 * @event initialize
20342 * Fires when the editor is fully initialized (including the iframe)
20343 * @param {Roo.HtmlEditorCore} this
20348 * Fires when the editor is first receives the focus. Any insertion must wait
20349 * until after this event.
20350 * @param {Roo.HtmlEditorCore} this
20354 * @event beforesync
20355 * Fires before the textarea is updated with content from the editor iframe. Return false
20356 * to cancel the sync.
20357 * @param {Roo.HtmlEditorCore} this
20358 * @param {String} html
20362 * @event beforepush
20363 * Fires before the iframe editor is updated with content from the textarea. Return false
20364 * to cancel the push.
20365 * @param {Roo.HtmlEditorCore} this
20366 * @param {String} html
20371 * Fires when the textarea is updated with content from the editor iframe.
20372 * @param {Roo.HtmlEditorCore} this
20373 * @param {String} html
20378 * Fires when the iframe editor is updated with content from the textarea.
20379 * @param {Roo.HtmlEditorCore} this
20380 * @param {String} html
20385 * @event editorevent
20386 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20387 * @param {Roo.HtmlEditorCore} this
20393 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20395 // defaults : white / black...
20396 this.applyBlacklists();
20403 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
20407 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
20413 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
20418 * @cfg {Number} height (in pixels)
20422 * @cfg {Number} width (in pixels)
20427 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20430 stylesheets: false,
20435 // private properties
20436 validationEvent : false,
20438 initialized : false,
20440 sourceEditMode : false,
20441 onFocus : Roo.emptyFn,
20443 hideMode:'offsets',
20447 // blacklist + whitelisted elements..
20454 * Protected method that will not generally be called directly. It
20455 * is called when the editor initializes the iframe with HTML contents. Override this method if you
20456 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20458 getDocMarkup : function(){
20462 // inherit styels from page...??
20463 if (this.stylesheets === false) {
20465 Roo.get(document.head).select('style').each(function(node) {
20466 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20469 Roo.get(document.head).select('link').each(function(node) {
20470 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20473 } else if (!this.stylesheets.length) {
20475 st = '<style type="text/css">' +
20476 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20482 st += '<style type="text/css">' +
20483 'IMG { cursor: pointer } ' +
20487 return '<html><head>' + st +
20488 //<style type="text/css">' +
20489 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20491 ' </head><body class="roo-htmleditor-body"></body></html>';
20495 onRender : function(ct, position)
20498 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20499 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20502 this.el.dom.style.border = '0 none';
20503 this.el.dom.setAttribute('tabIndex', -1);
20504 this.el.addClass('x-hidden hide');
20508 if(Roo.isIE){ // fix IE 1px bogus margin
20509 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20513 this.frameId = Roo.id();
20517 var iframe = this.owner.wrap.createChild({
20519 cls: 'form-control', // bootstrap..
20521 name: this.frameId,
20522 frameBorder : 'no',
20523 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
20528 this.iframe = iframe.dom;
20530 this.assignDocWin();
20532 this.doc.designMode = 'on';
20535 this.doc.write(this.getDocMarkup());
20539 var task = { // must defer to wait for browser to be ready
20541 //console.log("run task?" + this.doc.readyState);
20542 this.assignDocWin();
20543 if(this.doc.body || this.doc.readyState == 'complete'){
20545 this.doc.designMode="on";
20549 Roo.TaskMgr.stop(task);
20550 this.initEditor.defer(10, this);
20557 Roo.TaskMgr.start(task);
20562 onResize : function(w, h)
20564 Roo.log('resize: ' +w + ',' + h );
20565 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20569 if(typeof w == 'number'){
20571 this.iframe.style.width = w + 'px';
20573 if(typeof h == 'number'){
20575 this.iframe.style.height = h + 'px';
20577 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20584 * Toggles the editor between standard and source edit mode.
20585 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20587 toggleSourceEdit : function(sourceEditMode){
20589 this.sourceEditMode = sourceEditMode === true;
20591 if(this.sourceEditMode){
20593 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
20596 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20597 //this.iframe.className = '';
20600 //this.setSize(this.owner.wrap.getSize());
20601 //this.fireEvent('editmodechange', this, this.sourceEditMode);
20608 * Protected method that will not generally be called directly. If you need/want
20609 * custom HTML cleanup, this is the method you should override.
20610 * @param {String} html The HTML to be cleaned
20611 * return {String} The cleaned HTML
20613 cleanHtml : function(html){
20614 html = String(html);
20615 if(html.length > 5){
20616 if(Roo.isSafari){ // strip safari nonsense
20617 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20620 if(html == ' '){
20627 * HTML Editor -> Textarea
20628 * Protected method that will not generally be called directly. Syncs the contents
20629 * of the editor iframe with the textarea.
20631 syncValue : function(){
20632 if(this.initialized){
20633 var bd = (this.doc.body || this.doc.documentElement);
20634 //this.cleanUpPaste(); -- this is done else where and causes havoc..
20635 var html = bd.innerHTML;
20637 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20638 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20640 html = '<div style="'+m[0]+'">' + html + '</div>';
20643 html = this.cleanHtml(html);
20644 // fix up the special chars.. normaly like back quotes in word...
20645 // however we do not want to do this with chinese..
20646 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
20647 var cc = b.charCodeAt();
20649 (cc >= 0x4E00 && cc < 0xA000 ) ||
20650 (cc >= 0x3400 && cc < 0x4E00 ) ||
20651 (cc >= 0xf900 && cc < 0xfb00 )
20657 if(this.owner.fireEvent('beforesync', this, html) !== false){
20658 this.el.dom.value = html;
20659 this.owner.fireEvent('sync', this, html);
20665 * Protected method that will not generally be called directly. Pushes the value of the textarea
20666 * into the iframe editor.
20668 pushValue : function(){
20669 if(this.initialized){
20670 var v = this.el.dom.value.trim();
20672 // if(v.length < 1){
20676 if(this.owner.fireEvent('beforepush', this, v) !== false){
20677 var d = (this.doc.body || this.doc.documentElement);
20679 this.cleanUpPaste();
20680 this.el.dom.value = d.innerHTML;
20681 this.owner.fireEvent('push', this, v);
20687 deferFocus : function(){
20688 this.focus.defer(10, this);
20692 focus : function(){
20693 if(this.win && !this.sourceEditMode){
20700 assignDocWin: function()
20702 var iframe = this.iframe;
20705 this.doc = iframe.contentWindow.document;
20706 this.win = iframe.contentWindow;
20708 // if (!Roo.get(this.frameId)) {
20711 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20712 // this.win = Roo.get(this.frameId).dom.contentWindow;
20714 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20718 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20719 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20724 initEditor : function(){
20725 //console.log("INIT EDITOR");
20726 this.assignDocWin();
20730 this.doc.designMode="on";
20732 this.doc.write(this.getDocMarkup());
20735 var dbody = (this.doc.body || this.doc.documentElement);
20736 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20737 // this copies styles from the containing element into thsi one..
20738 // not sure why we need all of this..
20739 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20741 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20742 //ss['background-attachment'] = 'fixed'; // w3c
20743 dbody.bgProperties = 'fixed'; // ie
20744 //Roo.DomHelper.applyStyles(dbody, ss);
20745 Roo.EventManager.on(this.doc, {
20746 //'mousedown': this.onEditorEvent,
20747 'mouseup': this.onEditorEvent,
20748 'dblclick': this.onEditorEvent,
20749 'click': this.onEditorEvent,
20750 'keyup': this.onEditorEvent,
20755 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20757 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20758 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20760 this.initialized = true;
20762 this.owner.fireEvent('initialize', this);
20767 onDestroy : function(){
20773 //for (var i =0; i < this.toolbars.length;i++) {
20774 // // fixme - ask toolbars for heights?
20775 // this.toolbars[i].onDestroy();
20778 //this.wrap.dom.innerHTML = '';
20779 //this.wrap.remove();
20784 onFirstFocus : function(){
20786 this.assignDocWin();
20789 this.activated = true;
20792 if(Roo.isGecko){ // prevent silly gecko errors
20794 var s = this.win.getSelection();
20795 if(!s.focusNode || s.focusNode.nodeType != 3){
20796 var r = s.getRangeAt(0);
20797 r.selectNodeContents((this.doc.body || this.doc.documentElement));
20802 this.execCmd('useCSS', true);
20803 this.execCmd('styleWithCSS', false);
20806 this.owner.fireEvent('activate', this);
20810 adjustFont: function(btn){
20811 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
20812 //if(Roo.isSafari){ // safari
20815 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
20816 if(Roo.isSafari){ // safari
20817 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
20818 v = (v < 10) ? 10 : v;
20819 v = (v > 48) ? 48 : v;
20820 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
20825 v = Math.max(1, v+adjust);
20827 this.execCmd('FontSize', v );
20830 onEditorEvent : function(e)
20832 this.owner.fireEvent('editorevent', this, e);
20833 // this.updateToolbar();
20834 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
20837 insertTag : function(tg)
20839 // could be a bit smarter... -> wrap the current selected tRoo..
20840 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
20842 range = this.createRange(this.getSelection());
20843 var wrappingNode = this.doc.createElement(tg.toLowerCase());
20844 wrappingNode.appendChild(range.extractContents());
20845 range.insertNode(wrappingNode);
20852 this.execCmd("formatblock", tg);
20856 insertText : function(txt)
20860 var range = this.createRange();
20861 range.deleteContents();
20862 //alert(Sender.getAttribute('label'));
20864 range.insertNode(this.doc.createTextNode(txt));
20870 * Executes a Midas editor command on the editor document and performs necessary focus and
20871 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
20872 * @param {String} cmd The Midas command
20873 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20875 relayCmd : function(cmd, value){
20877 this.execCmd(cmd, value);
20878 this.owner.fireEvent('editorevent', this);
20879 //this.updateToolbar();
20880 this.owner.deferFocus();
20884 * Executes a Midas editor command directly on the editor document.
20885 * For visual commands, you should use {@link #relayCmd} instead.
20886 * <b>This should only be called after the editor is initialized.</b>
20887 * @param {String} cmd The Midas command
20888 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20890 execCmd : function(cmd, value){
20891 this.doc.execCommand(cmd, false, value === undefined ? null : value);
20898 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
20900 * @param {String} text | dom node..
20902 insertAtCursor : function(text)
20907 if(!this.activated){
20913 var r = this.doc.selection.createRange();
20924 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
20928 // from jquery ui (MIT licenced)
20930 var win = this.win;
20932 if (win.getSelection && win.getSelection().getRangeAt) {
20933 range = win.getSelection().getRangeAt(0);
20934 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
20935 range.insertNode(node);
20936 } else if (win.document.selection && win.document.selection.createRange) {
20937 // no firefox support
20938 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20939 win.document.selection.createRange().pasteHTML(txt);
20941 // no firefox support
20942 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20943 this.execCmd('InsertHTML', txt);
20952 mozKeyPress : function(e){
20954 var c = e.getCharCode(), cmd;
20957 c = String.fromCharCode(c).toLowerCase();
20971 this.cleanUpPaste.defer(100, this);
20979 e.preventDefault();
20987 fixKeys : function(){ // load time branching for fastest keydown performance
20989 return function(e){
20990 var k = e.getKey(), r;
20993 r = this.doc.selection.createRange();
20996 r.pasteHTML('    ');
21003 r = this.doc.selection.createRange();
21005 var target = r.parentElement();
21006 if(!target || target.tagName.toLowerCase() != 'li'){
21008 r.pasteHTML('<br />');
21014 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21015 this.cleanUpPaste.defer(100, this);
21021 }else if(Roo.isOpera){
21022 return function(e){
21023 var k = e.getKey();
21027 this.execCmd('InsertHTML','    ');
21030 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21031 this.cleanUpPaste.defer(100, this);
21036 }else if(Roo.isSafari){
21037 return function(e){
21038 var k = e.getKey();
21042 this.execCmd('InsertText','\t');
21046 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21047 this.cleanUpPaste.defer(100, this);
21055 getAllAncestors: function()
21057 var p = this.getSelectedNode();
21060 a.push(p); // push blank onto stack..
21061 p = this.getParentElement();
21065 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21069 a.push(this.doc.body);
21073 lastSelNode : false,
21076 getSelection : function()
21078 this.assignDocWin();
21079 return Roo.isIE ? this.doc.selection : this.win.getSelection();
21082 getSelectedNode: function()
21084 // this may only work on Gecko!!!
21086 // should we cache this!!!!
21091 var range = this.createRange(this.getSelection()).cloneRange();
21094 var parent = range.parentElement();
21096 var testRange = range.duplicate();
21097 testRange.moveToElementText(parent);
21098 if (testRange.inRange(range)) {
21101 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21104 parent = parent.parentElement;
21109 // is ancestor a text element.
21110 var ac = range.commonAncestorContainer;
21111 if (ac.nodeType == 3) {
21112 ac = ac.parentNode;
21115 var ar = ac.childNodes;
21118 var other_nodes = [];
21119 var has_other_nodes = false;
21120 for (var i=0;i<ar.length;i++) {
21121 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
21124 // fullly contained node.
21126 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21131 // probably selected..
21132 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21133 other_nodes.push(ar[i]);
21137 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
21142 has_other_nodes = true;
21144 if (!nodes.length && other_nodes.length) {
21145 nodes= other_nodes;
21147 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21153 createRange: function(sel)
21155 // this has strange effects when using with
21156 // top toolbar - not sure if it's a great idea.
21157 //this.editor.contentWindow.focus();
21158 if (typeof sel != "undefined") {
21160 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21162 return this.doc.createRange();
21165 return this.doc.createRange();
21168 getParentElement: function()
21171 this.assignDocWin();
21172 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21174 var range = this.createRange(sel);
21177 var p = range.commonAncestorContainer;
21178 while (p.nodeType == 3) { // text node
21189 * Range intersection.. the hard stuff...
21193 * [ -- selected range --- ]
21197 * if end is before start or hits it. fail.
21198 * if start is after end or hits it fail.
21200 * if either hits (but other is outside. - then it's not
21206 // @see http://www.thismuchiknow.co.uk/?p=64.
21207 rangeIntersectsNode : function(range, node)
21209 var nodeRange = node.ownerDocument.createRange();
21211 nodeRange.selectNode(node);
21213 nodeRange.selectNodeContents(node);
21216 var rangeStartRange = range.cloneRange();
21217 rangeStartRange.collapse(true);
21219 var rangeEndRange = range.cloneRange();
21220 rangeEndRange.collapse(false);
21222 var nodeStartRange = nodeRange.cloneRange();
21223 nodeStartRange.collapse(true);
21225 var nodeEndRange = nodeRange.cloneRange();
21226 nodeEndRange.collapse(false);
21228 return rangeStartRange.compareBoundaryPoints(
21229 Range.START_TO_START, nodeEndRange) == -1 &&
21230 rangeEndRange.compareBoundaryPoints(
21231 Range.START_TO_START, nodeStartRange) == 1;
21235 rangeCompareNode : function(range, node)
21237 var nodeRange = node.ownerDocument.createRange();
21239 nodeRange.selectNode(node);
21241 nodeRange.selectNodeContents(node);
21245 range.collapse(true);
21247 nodeRange.collapse(true);
21249 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21250 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
21252 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21254 var nodeIsBefore = ss == 1;
21255 var nodeIsAfter = ee == -1;
21257 if (nodeIsBefore && nodeIsAfter) {
21260 if (!nodeIsBefore && nodeIsAfter) {
21261 return 1; //right trailed.
21264 if (nodeIsBefore && !nodeIsAfter) {
21265 return 2; // left trailed.
21271 // private? - in a new class?
21272 cleanUpPaste : function()
21274 // cleans up the whole document..
21275 Roo.log('cleanuppaste');
21277 this.cleanUpChildren(this.doc.body);
21278 var clean = this.cleanWordChars(this.doc.body.innerHTML);
21279 if (clean != this.doc.body.innerHTML) {
21280 this.doc.body.innerHTML = clean;
21285 cleanWordChars : function(input) {// change the chars to hex code
21286 var he = Roo.HtmlEditorCore;
21288 var output = input;
21289 Roo.each(he.swapCodes, function(sw) {
21290 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21292 output = output.replace(swapper, sw[1]);
21299 cleanUpChildren : function (n)
21301 if (!n.childNodes.length) {
21304 for (var i = n.childNodes.length-1; i > -1 ; i--) {
21305 this.cleanUpChild(n.childNodes[i]);
21312 cleanUpChild : function (node)
21315 //console.log(node);
21316 if (node.nodeName == "#text") {
21317 // clean up silly Windows -- stuff?
21320 if (node.nodeName == "#comment") {
21321 node.parentNode.removeChild(node);
21322 // clean up silly Windows -- stuff?
21325 var lcname = node.tagName.toLowerCase();
21326 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21327 // whitelist of tags..
21329 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21331 node.parentNode.removeChild(node);
21336 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21338 // remove <a name=....> as rendering on yahoo mailer is borked with this.
21339 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21341 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21342 // remove_keep_children = true;
21345 if (remove_keep_children) {
21346 this.cleanUpChildren(node);
21347 // inserts everything just before this node...
21348 while (node.childNodes.length) {
21349 var cn = node.childNodes[0];
21350 node.removeChild(cn);
21351 node.parentNode.insertBefore(cn, node);
21353 node.parentNode.removeChild(node);
21357 if (!node.attributes || !node.attributes.length) {
21358 this.cleanUpChildren(node);
21362 function cleanAttr(n,v)
21365 if (v.match(/^\./) || v.match(/^\//)) {
21368 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
21371 if (v.match(/^#/)) {
21374 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21375 node.removeAttribute(n);
21379 var cwhite = this.cwhite;
21380 var cblack = this.cblack;
21382 function cleanStyle(n,v)
21384 if (v.match(/expression/)) { //XSS?? should we even bother..
21385 node.removeAttribute(n);
21389 var parts = v.split(/;/);
21392 Roo.each(parts, function(p) {
21393 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21397 var l = p.split(':').shift().replace(/\s+/g,'');
21398 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21400 if ( cwhite.length && cblack.indexOf(l) > -1) {
21401 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21402 //node.removeAttribute(n);
21406 // only allow 'c whitelisted system attributes'
21407 if ( cwhite.length && cwhite.indexOf(l) < 0) {
21408 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21409 //node.removeAttribute(n);
21419 if (clean.length) {
21420 node.setAttribute(n, clean.join(';'));
21422 node.removeAttribute(n);
21428 for (var i = node.attributes.length-1; i > -1 ; i--) {
21429 var a = node.attributes[i];
21432 if (a.name.toLowerCase().substr(0,2)=='on') {
21433 node.removeAttribute(a.name);
21436 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21437 node.removeAttribute(a.name);
21440 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21441 cleanAttr(a.name,a.value); // fixme..
21444 if (a.name == 'style') {
21445 cleanStyle(a.name,a.value);
21448 /// clean up MS crap..
21449 // tecnically this should be a list of valid class'es..
21452 if (a.name == 'class') {
21453 if (a.value.match(/^Mso/)) {
21454 node.className = '';
21457 if (a.value.match(/body/)) {
21458 node.className = '';
21469 this.cleanUpChildren(node);
21475 * Clean up MS wordisms...
21477 cleanWord : function(node)
21482 this.cleanWord(this.doc.body);
21485 if (node.nodeName == "#text") {
21486 // clean up silly Windows -- stuff?
21489 if (node.nodeName == "#comment") {
21490 node.parentNode.removeChild(node);
21491 // clean up silly Windows -- stuff?
21495 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21496 node.parentNode.removeChild(node);
21500 // remove - but keep children..
21501 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
21502 while (node.childNodes.length) {
21503 var cn = node.childNodes[0];
21504 node.removeChild(cn);
21505 node.parentNode.insertBefore(cn, node);
21507 node.parentNode.removeChild(node);
21508 this.iterateChildren(node, this.cleanWord);
21512 if (node.className.length) {
21514 var cn = node.className.split(/\W+/);
21516 Roo.each(cn, function(cls) {
21517 if (cls.match(/Mso[a-zA-Z]+/)) {
21522 node.className = cna.length ? cna.join(' ') : '';
21524 node.removeAttribute("class");
21528 if (node.hasAttribute("lang")) {
21529 node.removeAttribute("lang");
21532 if (node.hasAttribute("style")) {
21534 var styles = node.getAttribute("style").split(";");
21536 Roo.each(styles, function(s) {
21537 if (!s.match(/:/)) {
21540 var kv = s.split(":");
21541 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21544 // what ever is left... we allow.
21547 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21548 if (!nstyle.length) {
21549 node.removeAttribute('style');
21552 this.iterateChildren(node, this.cleanWord);
21558 * iterateChildren of a Node, calling fn each time, using this as the scole..
21559 * @param {DomNode} node node to iterate children of.
21560 * @param {Function} fn method of this class to call on each item.
21562 iterateChildren : function(node, fn)
21564 if (!node.childNodes.length) {
21567 for (var i = node.childNodes.length-1; i > -1 ; i--) {
21568 fn.call(this, node.childNodes[i])
21574 * cleanTableWidths.
21576 * Quite often pasting from word etc.. results in tables with column and widths.
21577 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21580 cleanTableWidths : function(node)
21585 this.cleanTableWidths(this.doc.body);
21590 if (node.nodeName == "#text" || node.nodeName == "#comment") {
21593 Roo.log(node.tagName);
21594 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21595 this.iterateChildren(node, this.cleanTableWidths);
21598 if (node.hasAttribute('width')) {
21599 node.removeAttribute('width');
21603 if (node.hasAttribute("style")) {
21606 var styles = node.getAttribute("style").split(";");
21608 Roo.each(styles, function(s) {
21609 if (!s.match(/:/)) {
21612 var kv = s.split(":");
21613 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21616 // what ever is left... we allow.
21619 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21620 if (!nstyle.length) {
21621 node.removeAttribute('style');
21625 this.iterateChildren(node, this.cleanTableWidths);
21633 domToHTML : function(currentElement, depth, nopadtext) {
21635 depth = depth || 0;
21636 nopadtext = nopadtext || false;
21638 if (!currentElement) {
21639 return this.domToHTML(this.doc.body);
21642 //Roo.log(currentElement);
21644 var allText = false;
21645 var nodeName = currentElement.nodeName;
21646 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21648 if (nodeName == '#text') {
21650 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21655 if (nodeName != 'BODY') {
21658 // Prints the node tagName, such as <A>, <IMG>, etc
21661 for(i = 0; i < currentElement.attributes.length;i++) {
21663 var aname = currentElement.attributes.item(i).name;
21664 if (!currentElement.attributes.item(i).value.length) {
21667 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21670 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21679 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21682 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21687 // Traverse the tree
21689 var currentElementChild = currentElement.childNodes.item(i);
21690 var allText = true;
21691 var innerHTML = '';
21693 while (currentElementChild) {
21694 // Formatting code (indent the tree so it looks nice on the screen)
21695 var nopad = nopadtext;
21696 if (lastnode == 'SPAN') {
21700 if (currentElementChild.nodeName == '#text') {
21701 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21702 toadd = nopadtext ? toadd : toadd.trim();
21703 if (!nopad && toadd.length > 80) {
21704 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
21706 innerHTML += toadd;
21709 currentElementChild = currentElement.childNodes.item(i);
21715 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
21717 // Recursively traverse the tree structure of the child node
21718 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
21719 lastnode = currentElementChild.nodeName;
21721 currentElementChild=currentElement.childNodes.item(i);
21727 // The remaining code is mostly for formatting the tree
21728 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
21733 ret+= "</"+tagName+">";
21739 applyBlacklists : function()
21741 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
21742 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
21746 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
21747 if (b.indexOf(tag) > -1) {
21750 this.white.push(tag);
21754 Roo.each(w, function(tag) {
21755 if (b.indexOf(tag) > -1) {
21758 if (this.white.indexOf(tag) > -1) {
21761 this.white.push(tag);
21766 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
21767 if (w.indexOf(tag) > -1) {
21770 this.black.push(tag);
21774 Roo.each(b, function(tag) {
21775 if (w.indexOf(tag) > -1) {
21778 if (this.black.indexOf(tag) > -1) {
21781 this.black.push(tag);
21786 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
21787 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
21791 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
21792 if (b.indexOf(tag) > -1) {
21795 this.cwhite.push(tag);
21799 Roo.each(w, function(tag) {
21800 if (b.indexOf(tag) > -1) {
21803 if (this.cwhite.indexOf(tag) > -1) {
21806 this.cwhite.push(tag);
21811 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
21812 if (w.indexOf(tag) > -1) {
21815 this.cblack.push(tag);
21819 Roo.each(b, function(tag) {
21820 if (w.indexOf(tag) > -1) {
21823 if (this.cblack.indexOf(tag) > -1) {
21826 this.cblack.push(tag);
21831 setStylesheets : function(stylesheets)
21833 if(typeof(stylesheets) == 'string'){
21834 Roo.get(this.iframe.contentDocument.head).createChild({
21836 rel : 'stylesheet',
21845 Roo.each(stylesheets, function(s) {
21850 Roo.get(_this.iframe.contentDocument.head).createChild({
21852 rel : 'stylesheet',
21861 removeStylesheets : function()
21865 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
21870 // hide stuff that is not compatible
21884 * @event specialkey
21888 * @cfg {String} fieldClass @hide
21891 * @cfg {String} focusClass @hide
21894 * @cfg {String} autoCreate @hide
21897 * @cfg {String} inputType @hide
21900 * @cfg {String} invalidClass @hide
21903 * @cfg {String} invalidText @hide
21906 * @cfg {String} msgFx @hide
21909 * @cfg {String} validateOnBlur @hide
21913 Roo.HtmlEditorCore.white = [
21914 'area', 'br', 'img', 'input', 'hr', 'wbr',
21916 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
21917 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
21918 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
21919 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
21920 'table', 'ul', 'xmp',
21922 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
21925 'dir', 'menu', 'ol', 'ul', 'dl',
21931 Roo.HtmlEditorCore.black = [
21932 // 'embed', 'object', // enable - backend responsiblity to clean thiese
21934 'base', 'basefont', 'bgsound', 'blink', 'body',
21935 'frame', 'frameset', 'head', 'html', 'ilayer',
21936 'iframe', 'layer', 'link', 'meta', 'object',
21937 'script', 'style' ,'title', 'xml' // clean later..
21939 Roo.HtmlEditorCore.clean = [
21940 'script', 'style', 'title', 'xml'
21942 Roo.HtmlEditorCore.remove = [
21947 Roo.HtmlEditorCore.ablack = [
21951 Roo.HtmlEditorCore.aclean = [
21952 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
21956 Roo.HtmlEditorCore.pwhite= [
21957 'http', 'https', 'mailto'
21960 // white listed style attributes.
21961 Roo.HtmlEditorCore.cwhite= [
21962 // 'text-align', /// default is to allow most things..
21968 // black listed style attributes.
21969 Roo.HtmlEditorCore.cblack= [
21970 // 'font-size' -- this can be set by the project
21974 Roo.HtmlEditorCore.swapCodes =[
21993 * @class Roo.bootstrap.HtmlEditor
21994 * @extends Roo.bootstrap.TextArea
21995 * Bootstrap HtmlEditor class
21998 * Create a new HtmlEditor
21999 * @param {Object} config The config object
22002 Roo.bootstrap.HtmlEditor = function(config){
22003 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
22004 if (!this.toolbars) {
22005 this.toolbars = [];
22007 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22010 * @event initialize
22011 * Fires when the editor is fully initialized (including the iframe)
22012 * @param {HtmlEditor} this
22017 * Fires when the editor is first receives the focus. Any insertion must wait
22018 * until after this event.
22019 * @param {HtmlEditor} this
22023 * @event beforesync
22024 * Fires before the textarea is updated with content from the editor iframe. Return false
22025 * to cancel the sync.
22026 * @param {HtmlEditor} this
22027 * @param {String} html
22031 * @event beforepush
22032 * Fires before the iframe editor is updated with content from the textarea. Return false
22033 * to cancel the push.
22034 * @param {HtmlEditor} this
22035 * @param {String} html
22040 * Fires when the textarea is updated with content from the editor iframe.
22041 * @param {HtmlEditor} this
22042 * @param {String} html
22047 * Fires when the iframe editor is updated with content from the textarea.
22048 * @param {HtmlEditor} this
22049 * @param {String} html
22053 * @event editmodechange
22054 * Fires when the editor switches edit modes
22055 * @param {HtmlEditor} this
22056 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22058 editmodechange: true,
22060 * @event editorevent
22061 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22062 * @param {HtmlEditor} this
22066 * @event firstfocus
22067 * Fires when on first focus - needed by toolbars..
22068 * @param {HtmlEditor} this
22073 * Auto save the htmlEditor value as a file into Events
22074 * @param {HtmlEditor} this
22078 * @event savedpreview
22079 * preview the saved version of htmlEditor
22080 * @param {HtmlEditor} this
22087 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
22091 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22096 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
22101 * @cfg {Number} height (in pixels)
22105 * @cfg {Number} width (in pixels)
22110 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22113 stylesheets: false,
22118 // private properties
22119 validationEvent : false,
22121 initialized : false,
22124 onFocus : Roo.emptyFn,
22126 hideMode:'offsets',
22129 tbContainer : false,
22131 toolbarContainer :function() {
22132 return this.wrap.select('.x-html-editor-tb',true).first();
22136 * Protected method that will not generally be called directly. It
22137 * is called when the editor creates its toolbar. Override this method if you need to
22138 * add custom toolbar buttons.
22139 * @param {HtmlEditor} editor
22141 createToolbar : function(){
22143 Roo.log("create toolbars");
22145 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
22146 this.toolbars[0].render(this.toolbarContainer());
22150 // if (!editor.toolbars || !editor.toolbars.length) {
22151 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
22154 // for (var i =0 ; i < editor.toolbars.length;i++) {
22155 // editor.toolbars[i] = Roo.factory(
22156 // typeof(editor.toolbars[i]) == 'string' ?
22157 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
22158 // Roo.bootstrap.HtmlEditor);
22159 // editor.toolbars[i].init(editor);
22165 onRender : function(ct, position)
22167 // Roo.log("Call onRender: " + this.xtype);
22169 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
22171 this.wrap = this.inputEl().wrap({
22172 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22175 this.editorcore.onRender(ct, position);
22177 if (this.resizable) {
22178 this.resizeEl = new Roo.Resizable(this.wrap, {
22182 minHeight : this.height,
22183 height: this.height,
22184 handles : this.resizable,
22187 resize : function(r, w, h) {
22188 _t.onResize(w,h); // -something
22194 this.createToolbar(this);
22197 if(!this.width && this.resizable){
22198 this.setSize(this.wrap.getSize());
22200 if (this.resizeEl) {
22201 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22202 // should trigger onReize..
22208 onResize : function(w, h)
22210 Roo.log('resize: ' +w + ',' + h );
22211 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
22215 if(this.inputEl() ){
22216 if(typeof w == 'number'){
22217 var aw = w - this.wrap.getFrameWidth('lr');
22218 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
22221 if(typeof h == 'number'){
22222 var tbh = -11; // fixme it needs to tool bar size!
22223 for (var i =0; i < this.toolbars.length;i++) {
22224 // fixme - ask toolbars for heights?
22225 tbh += this.toolbars[i].el.getHeight();
22226 //if (this.toolbars[i].footer) {
22227 // tbh += this.toolbars[i].footer.el.getHeight();
22235 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
22236 ah -= 5; // knock a few pixes off for look..
22237 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
22241 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
22242 this.editorcore.onResize(ew,eh);
22247 * Toggles the editor between standard and source edit mode.
22248 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22250 toggleSourceEdit : function(sourceEditMode)
22252 this.editorcore.toggleSourceEdit(sourceEditMode);
22254 if(this.editorcore.sourceEditMode){
22255 Roo.log('editor - showing textarea');
22258 // Roo.log(this.syncValue());
22260 this.inputEl().removeClass(['hide', 'x-hidden']);
22261 this.inputEl().dom.removeAttribute('tabIndex');
22262 this.inputEl().focus();
22264 Roo.log('editor - hiding textarea');
22266 // Roo.log(this.pushValue());
22269 this.inputEl().addClass(['hide', 'x-hidden']);
22270 this.inputEl().dom.setAttribute('tabIndex', -1);
22271 //this.deferFocus();
22274 if(this.resizable){
22275 this.setSize(this.wrap.getSize());
22278 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
22281 // private (for BoxComponent)
22282 adjustSize : Roo.BoxComponent.prototype.adjustSize,
22284 // private (for BoxComponent)
22285 getResizeEl : function(){
22289 // private (for BoxComponent)
22290 getPositionEl : function(){
22295 initEvents : function(){
22296 this.originalValue = this.getValue();
22300 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22303 // markInvalid : Roo.emptyFn,
22305 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22308 // clearInvalid : Roo.emptyFn,
22310 setValue : function(v){
22311 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
22312 this.editorcore.pushValue();
22317 deferFocus : function(){
22318 this.focus.defer(10, this);
22322 focus : function(){
22323 this.editorcore.focus();
22329 onDestroy : function(){
22335 for (var i =0; i < this.toolbars.length;i++) {
22336 // fixme - ask toolbars for heights?
22337 this.toolbars[i].onDestroy();
22340 this.wrap.dom.innerHTML = '';
22341 this.wrap.remove();
22346 onFirstFocus : function(){
22347 //Roo.log("onFirstFocus");
22348 this.editorcore.onFirstFocus();
22349 for (var i =0; i < this.toolbars.length;i++) {
22350 this.toolbars[i].onFirstFocus();
22356 syncValue : function()
22358 this.editorcore.syncValue();
22361 pushValue : function()
22363 this.editorcore.pushValue();
22367 // hide stuff that is not compatible
22381 * @event specialkey
22385 * @cfg {String} fieldClass @hide
22388 * @cfg {String} focusClass @hide
22391 * @cfg {String} autoCreate @hide
22394 * @cfg {String} inputType @hide
22397 * @cfg {String} invalidClass @hide
22400 * @cfg {String} invalidText @hide
22403 * @cfg {String} msgFx @hide
22406 * @cfg {String} validateOnBlur @hide
22415 Roo.namespace('Roo.bootstrap.htmleditor');
22417 * @class Roo.bootstrap.HtmlEditorToolbar1
22422 new Roo.bootstrap.HtmlEditor({
22425 new Roo.bootstrap.HtmlEditorToolbar1({
22426 disable : { fonts: 1 , format: 1, ..., ... , ...],
22432 * @cfg {Object} disable List of elements to disable..
22433 * @cfg {Array} btns List of additional buttons.
22437 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22440 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
22443 Roo.apply(this, config);
22445 // default disabled, based on 'good practice'..
22446 this.disable = this.disable || {};
22447 Roo.applyIf(this.disable, {
22450 specialElements : true
22452 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
22454 this.editor = config.editor;
22455 this.editorcore = config.editor.editorcore;
22457 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
22459 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22460 // dont call parent... till later.
22462 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
22467 editorcore : false,
22472 "h1","h2","h3","h4","h5","h6",
22474 "abbr", "acronym", "address", "cite", "samp", "var",
22478 onRender : function(ct, position)
22480 // Roo.log("Call onRender: " + this.xtype);
22482 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
22484 this.el.dom.style.marginBottom = '0';
22486 var editorcore = this.editorcore;
22487 var editor= this.editor;
22490 var btn = function(id,cmd , toggle, handler){
22492 var event = toggle ? 'toggle' : 'click';
22497 xns: Roo.bootstrap,
22500 enableToggle:toggle !== false,
22502 pressed : toggle ? false : null,
22505 a.listeners[toggle ? 'toggle' : 'click'] = function() {
22506 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
22515 xns: Roo.bootstrap,
22516 glyphicon : 'font',
22520 xns: Roo.bootstrap,
22524 Roo.each(this.formats, function(f) {
22525 style.menu.items.push({
22527 xns: Roo.bootstrap,
22528 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
22533 editorcore.insertTag(this.tagname);
22540 children.push(style);
22543 btn('bold',false,true);
22544 btn('italic',false,true);
22545 btn('align-left', 'justifyleft',true);
22546 btn('align-center', 'justifycenter',true);
22547 btn('align-right' , 'justifyright',true);
22548 btn('link', false, false, function(btn) {
22549 //Roo.log("create link?");
22550 var url = prompt(this.createLinkText, this.defaultLinkValue);
22551 if(url && url != 'http:/'+'/'){
22552 this.editorcore.relayCmd('createlink', url);
22555 btn('list','insertunorderedlist',true);
22556 btn('pencil', false,true, function(btn){
22559 this.toggleSourceEdit(btn.pressed);
22565 xns: Roo.bootstrap,
22570 xns: Roo.bootstrap,
22575 cog.menu.items.push({
22577 xns: Roo.bootstrap,
22578 html : Clean styles,
22583 editorcore.insertTag(this.tagname);
22592 this.xtype = 'NavSimplebar';
22594 for(var i=0;i< children.length;i++) {
22596 this.buttons.add(this.addxtypeChild(children[i]));
22600 editor.on('editorevent', this.updateToolbar, this);
22602 onBtnClick : function(id)
22604 this.editorcore.relayCmd(id);
22605 this.editorcore.focus();
22609 * Protected method that will not generally be called directly. It triggers
22610 * a toolbar update by reading the markup state of the current selection in the editor.
22612 updateToolbar: function(){
22614 if(!this.editorcore.activated){
22615 this.editor.onFirstFocus(); // is this neeed?
22619 var btns = this.buttons;
22620 var doc = this.editorcore.doc;
22621 btns.get('bold').setActive(doc.queryCommandState('bold'));
22622 btns.get('italic').setActive(doc.queryCommandState('italic'));
22623 //btns.get('underline').setActive(doc.queryCommandState('underline'));
22625 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
22626 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
22627 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
22629 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
22630 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
22633 var ans = this.editorcore.getAllAncestors();
22634 if (this.formatCombo) {
22637 var store = this.formatCombo.store;
22638 this.formatCombo.setValue("");
22639 for (var i =0; i < ans.length;i++) {
22640 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
22642 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
22650 // hides menus... - so this cant be on a menu...
22651 Roo.bootstrap.MenuMgr.hideAll();
22653 Roo.bootstrap.MenuMgr.hideAll();
22654 //this.editorsyncValue();
22656 onFirstFocus: function() {
22657 this.buttons.each(function(item){
22661 toggleSourceEdit : function(sourceEditMode){
22664 if(sourceEditMode){
22665 Roo.log("disabling buttons");
22666 this.buttons.each( function(item){
22667 if(item.cmd != 'pencil'){
22673 Roo.log("enabling buttons");
22674 if(this.editorcore.initialized){
22675 this.buttons.each( function(item){
22681 Roo.log("calling toggole on editor");
22682 // tell the editor that it's been pressed..
22683 this.editor.toggleSourceEdit(sourceEditMode);
22693 * @class Roo.bootstrap.Table.AbstractSelectionModel
22694 * @extends Roo.util.Observable
22695 * Abstract base class for grid SelectionModels. It provides the interface that should be
22696 * implemented by descendant classes. This class should not be directly instantiated.
22699 Roo.bootstrap.Table.AbstractSelectionModel = function(){
22700 this.locked = false;
22701 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
22705 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
22706 /** @ignore Called by the grid automatically. Do not call directly. */
22707 init : function(grid){
22713 * Locks the selections.
22716 this.locked = true;
22720 * Unlocks the selections.
22722 unlock : function(){
22723 this.locked = false;
22727 * Returns true if the selections are locked.
22728 * @return {Boolean}
22730 isLocked : function(){
22731 return this.locked;
22735 * @extends Roo.bootstrap.Table.AbstractSelectionModel
22736 * @class Roo.bootstrap.Table.RowSelectionModel
22737 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
22738 * It supports multiple selections and keyboard selection/navigation.
22740 * @param {Object} config
22743 Roo.bootstrap.Table.RowSelectionModel = function(config){
22744 Roo.apply(this, config);
22745 this.selections = new Roo.util.MixedCollection(false, function(o){
22750 this.lastActive = false;
22754 * @event selectionchange
22755 * Fires when the selection changes
22756 * @param {SelectionModel} this
22758 "selectionchange" : true,
22760 * @event afterselectionchange
22761 * Fires after the selection changes (eg. by key press or clicking)
22762 * @param {SelectionModel} this
22764 "afterselectionchange" : true,
22766 * @event beforerowselect
22767 * Fires when a row is selected being selected, return false to cancel.
22768 * @param {SelectionModel} this
22769 * @param {Number} rowIndex The selected index
22770 * @param {Boolean} keepExisting False if other selections will be cleared
22772 "beforerowselect" : true,
22775 * Fires when a row is selected.
22776 * @param {SelectionModel} this
22777 * @param {Number} rowIndex The selected index
22778 * @param {Roo.data.Record} r The record
22780 "rowselect" : true,
22782 * @event rowdeselect
22783 * Fires when a row is deselected.
22784 * @param {SelectionModel} this
22785 * @param {Number} rowIndex The selected index
22787 "rowdeselect" : true
22789 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
22790 this.locked = false;
22793 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
22795 * @cfg {Boolean} singleSelect
22796 * True to allow selection of only one row at a time (defaults to false)
22798 singleSelect : false,
22801 initEvents : function()
22804 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
22805 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
22806 //}else{ // allow click to work like normal
22807 // this.grid.on("rowclick", this.handleDragableRowClick, this);
22809 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
22810 this.grid.on("rowclick", this.handleMouseDown, this);
22812 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
22813 "up" : function(e){
22815 this.selectPrevious(e.shiftKey);
22816 }else if(this.last !== false && this.lastActive !== false){
22817 var last = this.last;
22818 this.selectRange(this.last, this.lastActive-1);
22819 this.grid.getView().focusRow(this.lastActive);
22820 if(last !== false){
22824 this.selectFirstRow();
22826 this.fireEvent("afterselectionchange", this);
22828 "down" : function(e){
22830 this.selectNext(e.shiftKey);
22831 }else if(this.last !== false && this.lastActive !== false){
22832 var last = this.last;
22833 this.selectRange(this.last, this.lastActive+1);
22834 this.grid.getView().focusRow(this.lastActive);
22835 if(last !== false){
22839 this.selectFirstRow();
22841 this.fireEvent("afterselectionchange", this);
22845 this.grid.store.on('load', function(){
22846 this.selections.clear();
22849 var view = this.grid.view;
22850 view.on("refresh", this.onRefresh, this);
22851 view.on("rowupdated", this.onRowUpdated, this);
22852 view.on("rowremoved", this.onRemove, this);
22857 onRefresh : function()
22859 var ds = this.grid.store, i, v = this.grid.view;
22860 var s = this.selections;
22861 s.each(function(r){
22862 if((i = ds.indexOfId(r.id)) != -1){
22871 onRemove : function(v, index, r){
22872 this.selections.remove(r);
22876 onRowUpdated : function(v, index, r){
22877 if(this.isSelected(r)){
22878 v.onRowSelect(index);
22884 * @param {Array} records The records to select
22885 * @param {Boolean} keepExisting (optional) True to keep existing selections
22887 selectRecords : function(records, keepExisting)
22890 this.clearSelections();
22892 var ds = this.grid.store;
22893 for(var i = 0, len = records.length; i < len; i++){
22894 this.selectRow(ds.indexOf(records[i]), true);
22899 * Gets the number of selected rows.
22902 getCount : function(){
22903 return this.selections.length;
22907 * Selects the first row in the grid.
22909 selectFirstRow : function(){
22914 * Select the last row.
22915 * @param {Boolean} keepExisting (optional) True to keep existing selections
22917 selectLastRow : function(keepExisting){
22918 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
22919 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
22923 * Selects the row immediately following the last selected row.
22924 * @param {Boolean} keepExisting (optional) True to keep existing selections
22926 selectNext : function(keepExisting)
22928 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
22929 this.selectRow(this.last+1, keepExisting);
22930 this.grid.getView().focusRow(this.last);
22935 * Selects the row that precedes the last selected row.
22936 * @param {Boolean} keepExisting (optional) True to keep existing selections
22938 selectPrevious : function(keepExisting){
22940 this.selectRow(this.last-1, keepExisting);
22941 this.grid.getView().focusRow(this.last);
22946 * Returns the selected records
22947 * @return {Array} Array of selected records
22949 getSelections : function(){
22950 return [].concat(this.selections.items);
22954 * Returns the first selected record.
22957 getSelected : function(){
22958 return this.selections.itemAt(0);
22963 * Clears all selections.
22965 clearSelections : function(fast)
22971 var ds = this.grid.store;
22972 var s = this.selections;
22973 s.each(function(r){
22974 this.deselectRow(ds.indexOfId(r.id));
22978 this.selections.clear();
22985 * Selects all rows.
22987 selectAll : function(){
22991 this.selections.clear();
22992 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
22993 this.selectRow(i, true);
22998 * Returns True if there is a selection.
22999 * @return {Boolean}
23001 hasSelection : function(){
23002 return this.selections.length > 0;
23006 * Returns True if the specified row is selected.
23007 * @param {Number/Record} record The record or index of the record to check
23008 * @return {Boolean}
23010 isSelected : function(index){
23011 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
23012 return (r && this.selections.key(r.id) ? true : false);
23016 * Returns True if the specified record id is selected.
23017 * @param {String} id The id of record to check
23018 * @return {Boolean}
23020 isIdSelected : function(id){
23021 return (this.selections.key(id) ? true : false);
23026 handleMouseDBClick : function(e, t){
23030 handleMouseDown : function(e, t)
23032 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
23033 if(this.isLocked() || rowIndex < 0 ){
23036 if(e.shiftKey && this.last !== false){
23037 var last = this.last;
23038 this.selectRange(last, rowIndex, e.ctrlKey);
23039 this.last = last; // reset the last
23043 var isSelected = this.isSelected(rowIndex);
23044 //Roo.log("select row:" + rowIndex);
23046 this.deselectRow(rowIndex);
23048 this.selectRow(rowIndex, true);
23052 if(e.button !== 0 && isSelected){
23053 alert('rowIndex 2: ' + rowIndex);
23054 view.focusRow(rowIndex);
23055 }else if(e.ctrlKey && isSelected){
23056 this.deselectRow(rowIndex);
23057 }else if(!isSelected){
23058 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
23059 view.focusRow(rowIndex);
23063 this.fireEvent("afterselectionchange", this);
23066 handleDragableRowClick : function(grid, rowIndex, e)
23068 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
23069 this.selectRow(rowIndex, false);
23070 grid.view.focusRow(rowIndex);
23071 this.fireEvent("afterselectionchange", this);
23076 * Selects multiple rows.
23077 * @param {Array} rows Array of the indexes of the row to select
23078 * @param {Boolean} keepExisting (optional) True to keep existing selections
23080 selectRows : function(rows, keepExisting){
23082 this.clearSelections();
23084 for(var i = 0, len = rows.length; i < len; i++){
23085 this.selectRow(rows[i], true);
23090 * Selects a range of rows. All rows in between startRow and endRow are also selected.
23091 * @param {Number} startRow The index of the first row in the range
23092 * @param {Number} endRow The index of the last row in the range
23093 * @param {Boolean} keepExisting (optional) True to retain existing selections
23095 selectRange : function(startRow, endRow, keepExisting){
23100 this.clearSelections();
23102 if(startRow <= endRow){
23103 for(var i = startRow; i <= endRow; i++){
23104 this.selectRow(i, true);
23107 for(var i = startRow; i >= endRow; i--){
23108 this.selectRow(i, true);
23114 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
23115 * @param {Number} startRow The index of the first row in the range
23116 * @param {Number} endRow The index of the last row in the range
23118 deselectRange : function(startRow, endRow, preventViewNotify){
23122 for(var i = startRow; i <= endRow; i++){
23123 this.deselectRow(i, preventViewNotify);
23129 * @param {Number} row The index of the row to select
23130 * @param {Boolean} keepExisting (optional) True to keep existing selections
23132 selectRow : function(index, keepExisting, preventViewNotify)
23134 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
23137 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
23138 if(!keepExisting || this.singleSelect){
23139 this.clearSelections();
23142 var r = this.grid.store.getAt(index);
23143 //console.log('selectRow - record id :' + r.id);
23145 this.selections.add(r);
23146 this.last = this.lastActive = index;
23147 if(!preventViewNotify){
23148 var proxy = new Roo.Element(
23149 this.grid.getRowDom(index)
23151 proxy.addClass('bg-info info');
23153 this.fireEvent("rowselect", this, index, r);
23154 this.fireEvent("selectionchange", this);
23160 * @param {Number} row The index of the row to deselect
23162 deselectRow : function(index, preventViewNotify)
23167 if(this.last == index){
23170 if(this.lastActive == index){
23171 this.lastActive = false;
23174 var r = this.grid.store.getAt(index);
23179 this.selections.remove(r);
23180 //.console.log('deselectRow - record id :' + r.id);
23181 if(!preventViewNotify){
23183 var proxy = new Roo.Element(
23184 this.grid.getRowDom(index)
23186 proxy.removeClass('bg-info info');
23188 this.fireEvent("rowdeselect", this, index);
23189 this.fireEvent("selectionchange", this);
23193 restoreLast : function(){
23195 this.last = this._last;
23200 acceptsNav : function(row, col, cm){
23201 return !cm.isHidden(col) && cm.isCellEditable(col, row);
23205 onEditorKey : function(field, e){
23206 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
23211 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
23213 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
23215 }else if(k == e.ENTER && !e.ctrlKey){
23219 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
23221 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
23223 }else if(k == e.ESC){
23227 g.startEditing(newCell[0], newCell[1]);
23233 * Ext JS Library 1.1.1
23234 * Copyright(c) 2006-2007, Ext JS, LLC.
23236 * Originally Released Under LGPL - original licence link has changed is not relivant.
23239 * <script type="text/javascript">
23243 * @class Roo.bootstrap.PagingToolbar
23244 * @extends Roo.bootstrap.NavSimplebar
23245 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
23247 * Create a new PagingToolbar
23248 * @param {Object} config The config object
23249 * @param {Roo.data.Store} store
23251 Roo.bootstrap.PagingToolbar = function(config)
23253 // old args format still supported... - xtype is prefered..
23254 // created from xtype...
23256 this.ds = config.dataSource;
23258 if (config.store && !this.ds) {
23259 this.store= Roo.factory(config.store, Roo.data);
23260 this.ds = this.store;
23261 this.ds.xmodule = this.xmodule || false;
23264 this.toolbarItems = [];
23265 if (config.items) {
23266 this.toolbarItems = config.items;
23269 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
23274 this.bind(this.ds);
23277 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
23281 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
23283 * @cfg {Roo.data.Store} dataSource
23284 * The underlying data store providing the paged data
23287 * @cfg {String/HTMLElement/Element} container
23288 * container The id or element that will contain the toolbar
23291 * @cfg {Boolean} displayInfo
23292 * True to display the displayMsg (defaults to false)
23295 * @cfg {Number} pageSize
23296 * The number of records to display per page (defaults to 20)
23300 * @cfg {String} displayMsg
23301 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
23303 displayMsg : 'Displaying {0} - {1} of {2}',
23305 * @cfg {String} emptyMsg
23306 * The message to display when no records are found (defaults to "No data to display")
23308 emptyMsg : 'No data to display',
23310 * Customizable piece of the default paging text (defaults to "Page")
23313 beforePageText : "Page",
23315 * Customizable piece of the default paging text (defaults to "of %0")
23318 afterPageText : "of {0}",
23320 * Customizable piece of the default paging text (defaults to "First Page")
23323 firstText : "First Page",
23325 * Customizable piece of the default paging text (defaults to "Previous Page")
23328 prevText : "Previous Page",
23330 * Customizable piece of the default paging text (defaults to "Next Page")
23333 nextText : "Next Page",
23335 * Customizable piece of the default paging text (defaults to "Last Page")
23338 lastText : "Last Page",
23340 * Customizable piece of the default paging text (defaults to "Refresh")
23343 refreshText : "Refresh",
23347 onRender : function(ct, position)
23349 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
23350 this.navgroup.parentId = this.id;
23351 this.navgroup.onRender(this.el, null);
23352 // add the buttons to the navgroup
23354 if(this.displayInfo){
23355 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
23356 this.displayEl = this.el.select('.x-paging-info', true).first();
23357 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
23358 // this.displayEl = navel.el.select('span',true).first();
23364 Roo.each(_this.buttons, function(e){ // this might need to use render????
23365 Roo.factory(e).onRender(_this.el, null);
23369 Roo.each(_this.toolbarItems, function(e) {
23370 _this.navgroup.addItem(e);
23374 this.first = this.navgroup.addItem({
23375 tooltip: this.firstText,
23377 icon : 'fa fa-backward',
23379 preventDefault: true,
23380 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
23383 this.prev = this.navgroup.addItem({
23384 tooltip: this.prevText,
23386 icon : 'fa fa-step-backward',
23388 preventDefault: true,
23389 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
23391 //this.addSeparator();
23394 var field = this.navgroup.addItem( {
23396 cls : 'x-paging-position',
23398 html : this.beforePageText +
23399 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
23400 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
23403 this.field = field.el.select('input', true).first();
23404 this.field.on("keydown", this.onPagingKeydown, this);
23405 this.field.on("focus", function(){this.dom.select();});
23408 this.afterTextEl = field.el.select('.x-paging-after',true).first();
23409 //this.field.setHeight(18);
23410 //this.addSeparator();
23411 this.next = this.navgroup.addItem({
23412 tooltip: this.nextText,
23414 html : ' <i class="fa fa-step-forward">',
23416 preventDefault: true,
23417 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
23419 this.last = this.navgroup.addItem({
23420 tooltip: this.lastText,
23421 icon : 'fa fa-forward',
23424 preventDefault: true,
23425 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
23427 //this.addSeparator();
23428 this.loading = this.navgroup.addItem({
23429 tooltip: this.refreshText,
23430 icon: 'fa fa-refresh',
23431 preventDefault: true,
23432 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
23438 updateInfo : function(){
23439 if(this.displayEl){
23440 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
23441 var msg = count == 0 ?
23445 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
23447 this.displayEl.update(msg);
23452 onLoad : function(ds, r, o){
23453 this.cursor = o.params ? o.params.start : 0;
23454 var d = this.getPageData(),
23458 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
23459 this.field.dom.value = ap;
23460 this.first.setDisabled(ap == 1);
23461 this.prev.setDisabled(ap == 1);
23462 this.next.setDisabled(ap == ps);
23463 this.last.setDisabled(ap == ps);
23464 this.loading.enable();
23469 getPageData : function(){
23470 var total = this.ds.getTotalCount();
23473 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
23474 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
23479 onLoadError : function(){
23480 this.loading.enable();
23484 onPagingKeydown : function(e){
23485 var k = e.getKey();
23486 var d = this.getPageData();
23488 var v = this.field.dom.value, pageNum;
23489 if(!v || isNaN(pageNum = parseInt(v, 10))){
23490 this.field.dom.value = d.activePage;
23493 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
23494 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23497 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))
23499 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
23500 this.field.dom.value = pageNum;
23501 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
23504 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
23506 var v = this.field.dom.value, pageNum;
23507 var increment = (e.shiftKey) ? 10 : 1;
23508 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
23511 if(!v || isNaN(pageNum = parseInt(v, 10))) {
23512 this.field.dom.value = d.activePage;
23515 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
23517 this.field.dom.value = parseInt(v, 10) + increment;
23518 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
23519 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23526 beforeLoad : function(){
23528 this.loading.disable();
23533 onClick : function(which){
23542 ds.load({params:{start: 0, limit: this.pageSize}});
23545 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
23548 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
23551 var total = ds.getTotalCount();
23552 var extra = total % this.pageSize;
23553 var lastStart = extra ? (total - extra) : total-this.pageSize;
23554 ds.load({params:{start: lastStart, limit: this.pageSize}});
23557 ds.load({params:{start: this.cursor, limit: this.pageSize}});
23563 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
23564 * @param {Roo.data.Store} store The data store to unbind
23566 unbind : function(ds){
23567 ds.un("beforeload", this.beforeLoad, this);
23568 ds.un("load", this.onLoad, this);
23569 ds.un("loadexception", this.onLoadError, this);
23570 ds.un("remove", this.updateInfo, this);
23571 ds.un("add", this.updateInfo, this);
23572 this.ds = undefined;
23576 * Binds the paging toolbar to the specified {@link Roo.data.Store}
23577 * @param {Roo.data.Store} store The data store to bind
23579 bind : function(ds){
23580 ds.on("beforeload", this.beforeLoad, this);
23581 ds.on("load", this.onLoad, this);
23582 ds.on("loadexception", this.onLoadError, this);
23583 ds.on("remove", this.updateInfo, this);
23584 ds.on("add", this.updateInfo, this);
23595 * @class Roo.bootstrap.MessageBar
23596 * @extends Roo.bootstrap.Component
23597 * Bootstrap MessageBar class
23598 * @cfg {String} html contents of the MessageBar
23599 * @cfg {String} weight (info | success | warning | danger) default info
23600 * @cfg {String} beforeClass insert the bar before the given class
23601 * @cfg {Boolean} closable (true | false) default false
23602 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
23605 * Create a new Element
23606 * @param {Object} config The config object
23609 Roo.bootstrap.MessageBar = function(config){
23610 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
23613 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
23619 beforeClass: 'bootstrap-sticky-wrap',
23621 getAutoCreate : function(){
23625 cls: 'alert alert-dismissable alert-' + this.weight,
23630 html: this.html || ''
23636 cfg.cls += ' alert-messages-fixed';
23650 onRender : function(ct, position)
23652 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
23655 var cfg = Roo.apply({}, this.getAutoCreate());
23659 cfg.cls += ' ' + this.cls;
23662 cfg.style = this.style;
23664 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
23666 this.el.setVisibilityMode(Roo.Element.DISPLAY);
23669 this.el.select('>button.close').on('click', this.hide, this);
23675 if (!this.rendered) {
23681 this.fireEvent('show', this);
23687 if (!this.rendered) {
23693 this.fireEvent('hide', this);
23696 update : function()
23698 // var e = this.el.dom.firstChild;
23700 // if(this.closable){
23701 // e = e.nextSibling;
23704 // e.data = this.html || '';
23706 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
23722 * @class Roo.bootstrap.Graph
23723 * @extends Roo.bootstrap.Component
23724 * Bootstrap Graph class
23728 @cfg {String} graphtype bar | vbar | pie
23729 @cfg {number} g_x coodinator | centre x (pie)
23730 @cfg {number} g_y coodinator | centre y (pie)
23731 @cfg {number} g_r radius (pie)
23732 @cfg {number} g_height height of the chart (respected by all elements in the set)
23733 @cfg {number} g_width width of the chart (respected by all elements in the set)
23734 @cfg {Object} title The title of the chart
23737 -opts (object) options for the chart
23739 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
23740 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
23742 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.
23743 o stacked (boolean) whether or not to tread values as in a stacked bar chart
23745 o stretch (boolean)
23747 -opts (object) options for the pie
23750 o startAngle (number)
23751 o endAngle (number)
23755 * Create a new Input
23756 * @param {Object} config The config object
23759 Roo.bootstrap.Graph = function(config){
23760 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
23766 * The img click event for the img.
23767 * @param {Roo.EventObject} e
23773 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
23784 //g_colors: this.colors,
23791 getAutoCreate : function(){
23802 onRender : function(ct,position){
23805 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
23807 if (typeof(Raphael) == 'undefined') {
23808 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
23812 this.raphael = Raphael(this.el.dom);
23814 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23815 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23816 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23817 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
23819 r.text(160, 10, "Single Series Chart").attr(txtattr);
23820 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
23821 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
23822 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
23824 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
23825 r.barchart(330, 10, 300, 220, data1);
23826 r.barchart(10, 250, 300, 220, data2, {stacked: true});
23827 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
23830 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23831 // r.barchart(30, 30, 560, 250, xdata, {
23832 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
23833 // axis : "0 0 1 1",
23834 // axisxlabels : xdata
23835 // //yvalues : cols,
23838 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23840 // this.load(null,xdata,{
23841 // axis : "0 0 1 1",
23842 // axisxlabels : xdata
23847 load : function(graphtype,xdata,opts)
23849 this.raphael.clear();
23851 graphtype = this.graphtype;
23856 var r = this.raphael,
23857 fin = function () {
23858 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
23860 fout = function () {
23861 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
23863 pfin = function() {
23864 this.sector.stop();
23865 this.sector.scale(1.1, 1.1, this.cx, this.cy);
23868 this.label[0].stop();
23869 this.label[0].attr({ r: 7.5 });
23870 this.label[1].attr({ "font-weight": 800 });
23873 pfout = function() {
23874 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
23877 this.label[0].animate({ r: 5 }, 500, "bounce");
23878 this.label[1].attr({ "font-weight": 400 });
23884 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23887 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23890 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
23891 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
23893 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
23900 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
23905 setTitle: function(o)
23910 initEvents: function() {
23913 this.el.on('click', this.onClick, this);
23917 onClick : function(e)
23919 Roo.log('img onclick');
23920 this.fireEvent('click', this, e);
23932 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23935 * @class Roo.bootstrap.dash.NumberBox
23936 * @extends Roo.bootstrap.Component
23937 * Bootstrap NumberBox class
23938 * @cfg {String} headline Box headline
23939 * @cfg {String} content Box content
23940 * @cfg {String} icon Box icon
23941 * @cfg {String} footer Footer text
23942 * @cfg {String} fhref Footer href
23945 * Create a new NumberBox
23946 * @param {Object} config The config object
23950 Roo.bootstrap.dash.NumberBox = function(config){
23951 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
23955 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
23964 getAutoCreate : function(){
23968 cls : 'small-box ',
23976 cls : 'roo-headline',
23977 html : this.headline
23981 cls : 'roo-content',
23982 html : this.content
23996 cls : 'ion ' + this.icon
24005 cls : 'small-box-footer',
24006 href : this.fhref || '#',
24010 cfg.cn.push(footer);
24017 onRender : function(ct,position){
24018 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
24025 setHeadline: function (value)
24027 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
24030 setFooter: function (value, href)
24032 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
24035 this.el.select('a.small-box-footer',true).first().attr('href', href);
24040 setContent: function (value)
24042 this.el.select('.roo-content',true).first().dom.innerHTML = value;
24045 initEvents: function()
24059 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24062 * @class Roo.bootstrap.dash.TabBox
24063 * @extends Roo.bootstrap.Component
24064 * Bootstrap TabBox class
24065 * @cfg {String} title Title of the TabBox
24066 * @cfg {String} icon Icon of the TabBox
24067 * @cfg {Boolean} showtabs (true|false) show the tabs default true
24068 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
24071 * Create a new TabBox
24072 * @param {Object} config The config object
24076 Roo.bootstrap.dash.TabBox = function(config){
24077 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
24082 * When a pane is added
24083 * @param {Roo.bootstrap.dash.TabPane} pane
24087 * @event activatepane
24088 * When a pane is activated
24089 * @param {Roo.bootstrap.dash.TabPane} pane
24091 "activatepane" : true
24099 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
24104 tabScrollable : false,
24106 getChildContainer : function()
24108 return this.el.select('.tab-content', true).first();
24111 getAutoCreate : function(){
24115 cls: 'pull-left header',
24123 cls: 'fa ' + this.icon
24129 cls: 'nav nav-tabs pull-right',
24135 if(this.tabScrollable){
24142 cls: 'nav nav-tabs pull-right',
24153 cls: 'nav-tabs-custom',
24158 cls: 'tab-content no-padding',
24166 initEvents : function()
24168 //Roo.log('add add pane handler');
24169 this.on('addpane', this.onAddPane, this);
24172 * Updates the box title
24173 * @param {String} html to set the title to.
24175 setTitle : function(value)
24177 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
24179 onAddPane : function(pane)
24181 this.panes.push(pane);
24182 //Roo.log('addpane');
24184 // tabs are rendere left to right..
24185 if(!this.showtabs){
24189 var ctr = this.el.select('.nav-tabs', true).first();
24192 var existing = ctr.select('.nav-tab',true);
24193 var qty = existing.getCount();;
24196 var tab = ctr.createChild({
24198 cls : 'nav-tab' + (qty ? '' : ' active'),
24206 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
24209 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
24211 pane.el.addClass('active');
24216 onTabClick : function(ev,un,ob,pane)
24218 //Roo.log('tab - prev default');
24219 ev.preventDefault();
24222 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
24223 pane.tab.addClass('active');
24224 //Roo.log(pane.title);
24225 this.getChildContainer().select('.tab-pane',true).removeClass('active');
24226 // technically we should have a deactivate event.. but maybe add later.
24227 // and it should not de-activate the selected tab...
24228 this.fireEvent('activatepane', pane);
24229 pane.el.addClass('active');
24230 pane.fireEvent('activate');
24235 getActivePane : function()
24238 Roo.each(this.panes, function(p) {
24239 if(p.el.hasClass('active')){
24260 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24262 * @class Roo.bootstrap.TabPane
24263 * @extends Roo.bootstrap.Component
24264 * Bootstrap TabPane class
24265 * @cfg {Boolean} active (false | true) Default false
24266 * @cfg {String} title title of panel
24270 * Create a new TabPane
24271 * @param {Object} config The config object
24274 Roo.bootstrap.dash.TabPane = function(config){
24275 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
24281 * When a pane is activated
24282 * @param {Roo.bootstrap.dash.TabPane} pane
24289 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
24294 // the tabBox that this is attached to.
24297 getAutoCreate : function()
24305 cfg.cls += ' active';
24310 initEvents : function()
24312 //Roo.log('trigger add pane handler');
24313 this.parent().fireEvent('addpane', this)
24317 * Updates the tab title
24318 * @param {String} html to set the title to.
24320 setTitle: function(str)
24326 this.tab.select('a', true).first().dom.innerHTML = str;
24343 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24346 * @class Roo.bootstrap.menu.Menu
24347 * @extends Roo.bootstrap.Component
24348 * Bootstrap Menu class - container for Menu
24349 * @cfg {String} html Text of the menu
24350 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
24351 * @cfg {String} icon Font awesome icon
24352 * @cfg {String} pos Menu align to (top | bottom) default bottom
24356 * Create a new Menu
24357 * @param {Object} config The config object
24361 Roo.bootstrap.menu.Menu = function(config){
24362 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
24366 * @event beforeshow
24367 * Fires before this menu is displayed
24368 * @param {Roo.bootstrap.menu.Menu} this
24372 * @event beforehide
24373 * Fires before this menu is hidden
24374 * @param {Roo.bootstrap.menu.Menu} this
24379 * Fires after this menu is displayed
24380 * @param {Roo.bootstrap.menu.Menu} this
24385 * Fires after this menu is hidden
24386 * @param {Roo.bootstrap.menu.Menu} this
24391 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
24392 * @param {Roo.bootstrap.menu.Menu} this
24393 * @param {Roo.EventObject} e
24400 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
24404 weight : 'default',
24409 getChildContainer : function() {
24410 if(this.isSubMenu){
24414 return this.el.select('ul.dropdown-menu', true).first();
24417 getAutoCreate : function()
24422 cls : 'roo-menu-text',
24430 cls : 'fa ' + this.icon
24441 cls : 'dropdown-button btn btn-' + this.weight,
24446 cls : 'dropdown-toggle btn btn-' + this.weight,
24456 cls : 'dropdown-menu'
24462 if(this.pos == 'top'){
24463 cfg.cls += ' dropup';
24466 if(this.isSubMenu){
24469 cls : 'dropdown-menu'
24476 onRender : function(ct, position)
24478 this.isSubMenu = ct.hasClass('dropdown-submenu');
24480 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
24483 initEvents : function()
24485 if(this.isSubMenu){
24489 this.hidden = true;
24491 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
24492 this.triggerEl.on('click', this.onTriggerPress, this);
24494 this.buttonEl = this.el.select('button.dropdown-button', true).first();
24495 this.buttonEl.on('click', this.onClick, this);
24501 if(this.isSubMenu){
24505 return this.el.select('ul.dropdown-menu', true).first();
24508 onClick : function(e)
24510 this.fireEvent("click", this, e);
24513 onTriggerPress : function(e)
24515 if (this.isVisible()) {
24522 isVisible : function(){
24523 return !this.hidden;
24528 this.fireEvent("beforeshow", this);
24530 this.hidden = false;
24531 this.el.addClass('open');
24533 Roo.get(document).on("mouseup", this.onMouseUp, this);
24535 this.fireEvent("show", this);
24542 this.fireEvent("beforehide", this);
24544 this.hidden = true;
24545 this.el.removeClass('open');
24547 Roo.get(document).un("mouseup", this.onMouseUp);
24549 this.fireEvent("hide", this);
24552 onMouseUp : function()
24566 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24569 * @class Roo.bootstrap.menu.Item
24570 * @extends Roo.bootstrap.Component
24571 * Bootstrap MenuItem class
24572 * @cfg {Boolean} submenu (true | false) default false
24573 * @cfg {String} html text of the item
24574 * @cfg {String} href the link
24575 * @cfg {Boolean} disable (true | false) default false
24576 * @cfg {Boolean} preventDefault (true | false) default true
24577 * @cfg {String} icon Font awesome icon
24578 * @cfg {String} pos Submenu align to (left | right) default right
24582 * Create a new Item
24583 * @param {Object} config The config object
24587 Roo.bootstrap.menu.Item = function(config){
24588 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
24592 * Fires when the mouse is hovering over this menu
24593 * @param {Roo.bootstrap.menu.Item} this
24594 * @param {Roo.EventObject} e
24599 * Fires when the mouse exits this menu
24600 * @param {Roo.bootstrap.menu.Item} this
24601 * @param {Roo.EventObject} e
24607 * The raw click event for the entire grid.
24608 * @param {Roo.EventObject} e
24614 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
24619 preventDefault: true,
24624 getAutoCreate : function()
24629 cls : 'roo-menu-item-text',
24637 cls : 'fa ' + this.icon
24646 href : this.href || '#',
24653 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
24657 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
24659 if(this.pos == 'left'){
24660 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
24667 initEvents : function()
24669 this.el.on('mouseover', this.onMouseOver, this);
24670 this.el.on('mouseout', this.onMouseOut, this);
24672 this.el.select('a', true).first().on('click', this.onClick, this);
24676 onClick : function(e)
24678 if(this.preventDefault){
24679 e.preventDefault();
24682 this.fireEvent("click", this, e);
24685 onMouseOver : function(e)
24687 if(this.submenu && this.pos == 'left'){
24688 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
24691 this.fireEvent("mouseover", this, e);
24694 onMouseOut : function(e)
24696 this.fireEvent("mouseout", this, e);
24708 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24711 * @class Roo.bootstrap.menu.Separator
24712 * @extends Roo.bootstrap.Component
24713 * Bootstrap Separator class
24716 * Create a new Separator
24717 * @param {Object} config The config object
24721 Roo.bootstrap.menu.Separator = function(config){
24722 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
24725 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
24727 getAutoCreate : function(){
24748 * @class Roo.bootstrap.Tooltip
24749 * Bootstrap Tooltip class
24750 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
24751 * to determine which dom element triggers the tooltip.
24753 * It needs to add support for additional attributes like tooltip-position
24756 * Create a new Toolti
24757 * @param {Object} config The config object
24760 Roo.bootstrap.Tooltip = function(config){
24761 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
24764 Roo.apply(Roo.bootstrap.Tooltip, {
24766 * @function init initialize tooltip monitoring.
24770 currentTip : false,
24771 currentRegion : false,
24777 Roo.get(document).on('mouseover', this.enter ,this);
24778 Roo.get(document).on('mouseout', this.leave, this);
24781 this.currentTip = new Roo.bootstrap.Tooltip();
24784 enter : function(ev)
24786 var dom = ev.getTarget();
24788 //Roo.log(['enter',dom]);
24789 var el = Roo.fly(dom);
24790 if (this.currentEl) {
24792 //Roo.log(this.currentEl);
24793 //Roo.log(this.currentEl.contains(dom));
24794 if (this.currentEl == el) {
24797 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
24803 if (this.currentTip.el) {
24804 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
24808 if(!el || el.dom == document){
24814 // you can not look for children, as if el is the body.. then everythign is the child..
24815 if (!el.attr('tooltip')) { //
24816 if (!el.select("[tooltip]").elements.length) {
24819 // is the mouse over this child...?
24820 bindEl = el.select("[tooltip]").first();
24821 var xy = ev.getXY();
24822 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
24823 //Roo.log("not in region.");
24826 //Roo.log("child element over..");
24829 this.currentEl = bindEl;
24830 this.currentTip.bind(bindEl);
24831 this.currentRegion = Roo.lib.Region.getRegion(dom);
24832 this.currentTip.enter();
24835 leave : function(ev)
24837 var dom = ev.getTarget();
24838 //Roo.log(['leave',dom]);
24839 if (!this.currentEl) {
24844 if (dom != this.currentEl.dom) {
24847 var xy = ev.getXY();
24848 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
24851 // only activate leave if mouse cursor is outside... bounding box..
24856 if (this.currentTip) {
24857 this.currentTip.leave();
24859 //Roo.log('clear currentEl');
24860 this.currentEl = false;
24865 'left' : ['r-l', [-2,0], 'right'],
24866 'right' : ['l-r', [2,0], 'left'],
24867 'bottom' : ['t-b', [0,2], 'top'],
24868 'top' : [ 'b-t', [0,-2], 'bottom']
24874 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
24879 delay : null, // can be { show : 300 , hide: 500}
24883 hoverState : null, //???
24885 placement : 'bottom',
24887 getAutoCreate : function(){
24894 cls : 'tooltip-arrow'
24897 cls : 'tooltip-inner'
24904 bind : function(el)
24910 enter : function () {
24912 if (this.timeout != null) {
24913 clearTimeout(this.timeout);
24916 this.hoverState = 'in';
24917 //Roo.log("enter - show");
24918 if (!this.delay || !this.delay.show) {
24923 this.timeout = setTimeout(function () {
24924 if (_t.hoverState == 'in') {
24927 }, this.delay.show);
24931 clearTimeout(this.timeout);
24933 this.hoverState = 'out';
24934 if (!this.delay || !this.delay.hide) {
24940 this.timeout = setTimeout(function () {
24941 //Roo.log("leave - timeout");
24943 if (_t.hoverState == 'out') {
24945 Roo.bootstrap.Tooltip.currentEl = false;
24953 this.render(document.body);
24956 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
24958 var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
24960 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
24962 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
24964 var placement = typeof this.placement == 'function' ?
24965 this.placement.call(this, this.el, on_el) :
24968 var autoToken = /\s?auto?\s?/i;
24969 var autoPlace = autoToken.test(placement);
24971 placement = placement.replace(autoToken, '') || 'top';
24975 //this.el.setXY([0,0]);
24977 //this.el.dom.style.display='block';
24979 //this.el.appendTo(on_el);
24981 var p = this.getPosition();
24982 var box = this.el.getBox();
24988 var align = Roo.bootstrap.Tooltip.alignment[placement];
24990 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
24992 if(placement == 'top' || placement == 'bottom'){
24994 placement = 'right';
24997 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
24998 placement = 'left';
25001 var scroll = Roo.select('body', true).first().getScroll();
25003 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
25009 align = Roo.bootstrap.Tooltip.alignment[placement];
25011 this.el.alignTo(this.bindEl, align[0],align[1]);
25012 //var arrow = this.el.select('.arrow',true).first();
25013 //arrow.set(align[2],
25015 this.el.addClass(placement);
25017 this.el.addClass('in fade');
25019 this.hoverState = null;
25021 if (this.el.hasClass('fade')) {
25032 //this.el.setXY([0,0]);
25033 this.el.removeClass('in');
25049 * @class Roo.bootstrap.LocationPicker
25050 * @extends Roo.bootstrap.Component
25051 * Bootstrap LocationPicker class
25052 * @cfg {Number} latitude Position when init default 0
25053 * @cfg {Number} longitude Position when init default 0
25054 * @cfg {Number} zoom default 15
25055 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
25056 * @cfg {Boolean} mapTypeControl default false
25057 * @cfg {Boolean} disableDoubleClickZoom default false
25058 * @cfg {Boolean} scrollwheel default true
25059 * @cfg {Boolean} streetViewControl default false
25060 * @cfg {Number} radius default 0
25061 * @cfg {String} locationName
25062 * @cfg {Boolean} draggable default true
25063 * @cfg {Boolean} enableAutocomplete default false
25064 * @cfg {Boolean} enableReverseGeocode default true
25065 * @cfg {String} markerTitle
25068 * Create a new LocationPicker
25069 * @param {Object} config The config object
25073 Roo.bootstrap.LocationPicker = function(config){
25075 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
25080 * Fires when the picker initialized.
25081 * @param {Roo.bootstrap.LocationPicker} this
25082 * @param {Google Location} location
25086 * @event positionchanged
25087 * Fires when the picker position changed.
25088 * @param {Roo.bootstrap.LocationPicker} this
25089 * @param {Google Location} location
25091 positionchanged : true,
25094 * Fires when the map resize.
25095 * @param {Roo.bootstrap.LocationPicker} this
25100 * Fires when the map show.
25101 * @param {Roo.bootstrap.LocationPicker} this
25106 * Fires when the map hide.
25107 * @param {Roo.bootstrap.LocationPicker} this
25112 * Fires when click the map.
25113 * @param {Roo.bootstrap.LocationPicker} this
25114 * @param {Map event} e
25118 * @event mapRightClick
25119 * Fires when right click the map.
25120 * @param {Roo.bootstrap.LocationPicker} this
25121 * @param {Map event} e
25123 mapRightClick : true,
25125 * @event markerClick
25126 * Fires when click the marker.
25127 * @param {Roo.bootstrap.LocationPicker} this
25128 * @param {Map event} e
25130 markerClick : true,
25132 * @event markerRightClick
25133 * Fires when right click the marker.
25134 * @param {Roo.bootstrap.LocationPicker} this
25135 * @param {Map event} e
25137 markerRightClick : true,
25139 * @event OverlayViewDraw
25140 * Fires when OverlayView Draw
25141 * @param {Roo.bootstrap.LocationPicker} this
25143 OverlayViewDraw : true,
25145 * @event OverlayViewOnAdd
25146 * Fires when OverlayView Draw
25147 * @param {Roo.bootstrap.LocationPicker} this
25149 OverlayViewOnAdd : true,
25151 * @event OverlayViewOnRemove
25152 * Fires when OverlayView Draw
25153 * @param {Roo.bootstrap.LocationPicker} this
25155 OverlayViewOnRemove : true,
25157 * @event OverlayViewShow
25158 * Fires when OverlayView Draw
25159 * @param {Roo.bootstrap.LocationPicker} this
25160 * @param {Pixel} cpx
25162 OverlayViewShow : true,
25164 * @event OverlayViewHide
25165 * Fires when OverlayView Draw
25166 * @param {Roo.bootstrap.LocationPicker} this
25168 OverlayViewHide : true,
25170 * @event loadexception
25171 * Fires when load google lib failed.
25172 * @param {Roo.bootstrap.LocationPicker} this
25174 loadexception : true
25179 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
25181 gMapContext: false,
25187 mapTypeControl: false,
25188 disableDoubleClickZoom: false,
25190 streetViewControl: false,
25194 enableAutocomplete: false,
25195 enableReverseGeocode: true,
25198 getAutoCreate: function()
25203 cls: 'roo-location-picker'
25209 initEvents: function(ct, position)
25211 if(!this.el.getWidth() || this.isApplied()){
25215 this.el.setVisibilityMode(Roo.Element.DISPLAY);
25220 initial: function()
25222 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
25223 this.fireEvent('loadexception', this);
25227 if(!this.mapTypeId){
25228 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
25231 this.gMapContext = this.GMapContext();
25233 this.initOverlayView();
25235 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
25239 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
25240 _this.setPosition(_this.gMapContext.marker.position);
25243 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
25244 _this.fireEvent('mapClick', this, event);
25248 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
25249 _this.fireEvent('mapRightClick', this, event);
25253 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
25254 _this.fireEvent('markerClick', this, event);
25258 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
25259 _this.fireEvent('markerRightClick', this, event);
25263 this.setPosition(this.gMapContext.location);
25265 this.fireEvent('initial', this, this.gMapContext.location);
25268 initOverlayView: function()
25272 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
25276 _this.fireEvent('OverlayViewDraw', _this);
25281 _this.fireEvent('OverlayViewOnAdd', _this);
25284 onRemove: function()
25286 _this.fireEvent('OverlayViewOnRemove', _this);
25289 show: function(cpx)
25291 _this.fireEvent('OverlayViewShow', _this, cpx);
25296 _this.fireEvent('OverlayViewHide', _this);
25302 fromLatLngToContainerPixel: function(event)
25304 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
25307 isApplied: function()
25309 return this.getGmapContext() == false ? false : true;
25312 getGmapContext: function()
25314 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
25317 GMapContext: function()
25319 var position = new google.maps.LatLng(this.latitude, this.longitude);
25321 var _map = new google.maps.Map(this.el.dom, {
25324 mapTypeId: this.mapTypeId,
25325 mapTypeControl: this.mapTypeControl,
25326 disableDoubleClickZoom: this.disableDoubleClickZoom,
25327 scrollwheel: this.scrollwheel,
25328 streetViewControl: this.streetViewControl,
25329 locationName: this.locationName,
25330 draggable: this.draggable,
25331 enableAutocomplete: this.enableAutocomplete,
25332 enableReverseGeocode: this.enableReverseGeocode
25335 var _marker = new google.maps.Marker({
25336 position: position,
25338 title: this.markerTitle,
25339 draggable: this.draggable
25346 location: position,
25347 radius: this.radius,
25348 locationName: this.locationName,
25349 addressComponents: {
25350 formatted_address: null,
25351 addressLine1: null,
25352 addressLine2: null,
25354 streetNumber: null,
25358 stateOrProvince: null
25361 domContainer: this.el.dom,
25362 geodecoder: new google.maps.Geocoder()
25366 drawCircle: function(center, radius, options)
25368 if (this.gMapContext.circle != null) {
25369 this.gMapContext.circle.setMap(null);
25373 options = Roo.apply({}, options, {
25374 strokeColor: "#0000FF",
25375 strokeOpacity: .35,
25377 fillColor: "#0000FF",
25381 options.map = this.gMapContext.map;
25382 options.radius = radius;
25383 options.center = center;
25384 this.gMapContext.circle = new google.maps.Circle(options);
25385 return this.gMapContext.circle;
25391 setPosition: function(location)
25393 this.gMapContext.location = location;
25394 this.gMapContext.marker.setPosition(location);
25395 this.gMapContext.map.panTo(location);
25396 this.drawCircle(location, this.gMapContext.radius, {});
25400 if (this.gMapContext.settings.enableReverseGeocode) {
25401 this.gMapContext.geodecoder.geocode({
25402 latLng: this.gMapContext.location
25403 }, function(results, status) {
25405 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
25406 _this.gMapContext.locationName = results[0].formatted_address;
25407 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
25409 _this.fireEvent('positionchanged', this, location);
25416 this.fireEvent('positionchanged', this, location);
25421 google.maps.event.trigger(this.gMapContext.map, "resize");
25423 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
25425 this.fireEvent('resize', this);
25428 setPositionByLatLng: function(latitude, longitude)
25430 this.setPosition(new google.maps.LatLng(latitude, longitude));
25433 getCurrentPosition: function()
25436 latitude: this.gMapContext.location.lat(),
25437 longitude: this.gMapContext.location.lng()
25441 getAddressName: function()
25443 return this.gMapContext.locationName;
25446 getAddressComponents: function()
25448 return this.gMapContext.addressComponents;
25451 address_component_from_google_geocode: function(address_components)
25455 for (var i = 0; i < address_components.length; i++) {
25456 var component = address_components[i];
25457 if (component.types.indexOf("postal_code") >= 0) {
25458 result.postalCode = component.short_name;
25459 } else if (component.types.indexOf("street_number") >= 0) {
25460 result.streetNumber = component.short_name;
25461 } else if (component.types.indexOf("route") >= 0) {
25462 result.streetName = component.short_name;
25463 } else if (component.types.indexOf("neighborhood") >= 0) {
25464 result.city = component.short_name;
25465 } else if (component.types.indexOf("locality") >= 0) {
25466 result.city = component.short_name;
25467 } else if (component.types.indexOf("sublocality") >= 0) {
25468 result.district = component.short_name;
25469 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
25470 result.stateOrProvince = component.short_name;
25471 } else if (component.types.indexOf("country") >= 0) {
25472 result.country = component.short_name;
25476 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
25477 result.addressLine2 = "";
25481 setZoomLevel: function(zoom)
25483 this.gMapContext.map.setZoom(zoom);
25496 this.fireEvent('show', this);
25507 this.fireEvent('hide', this);
25512 Roo.apply(Roo.bootstrap.LocationPicker, {
25514 OverlayView : function(map, options)
25516 options = options || {};
25530 * @class Roo.bootstrap.Alert
25531 * @extends Roo.bootstrap.Component
25532 * Bootstrap Alert class
25533 * @cfg {String} title The title of alert
25534 * @cfg {String} html The content of alert
25535 * @cfg {String} weight ( success | info | warning | danger )
25536 * @cfg {String} faicon font-awesomeicon
25539 * Create a new alert
25540 * @param {Object} config The config object
25544 Roo.bootstrap.Alert = function(config){
25545 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
25549 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
25556 getAutoCreate : function()
25565 cls : 'roo-alert-icon'
25570 cls : 'roo-alert-title',
25575 cls : 'roo-alert-text',
25582 cfg.cn[0].cls += ' fa ' + this.faicon;
25586 cfg.cls += ' alert-' + this.weight;
25592 initEvents: function()
25594 this.el.setVisibilityMode(Roo.Element.DISPLAY);
25597 setTitle : function(str)
25599 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
25602 setText : function(str)
25604 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
25607 setWeight : function(weight)
25610 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
25613 this.weight = weight;
25615 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
25618 setIcon : function(icon)
25621 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
25624 this.faicon = icon;
25626 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
25647 * @class Roo.bootstrap.UploadCropbox
25648 * @extends Roo.bootstrap.Component
25649 * Bootstrap UploadCropbox class
25650 * @cfg {String} emptyText show when image has been loaded
25651 * @cfg {String} rotateNotify show when image too small to rotate
25652 * @cfg {Number} errorTimeout default 3000
25653 * @cfg {Number} minWidth default 300
25654 * @cfg {Number} minHeight default 300
25655 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
25656 * @cfg {Boolean} isDocument (true|false) default false
25657 * @cfg {String} url action url
25658 * @cfg {String} paramName default 'imageUpload'
25659 * @cfg {String} method default POST
25660 * @cfg {Boolean} loadMask (true|false) default true
25661 * @cfg {Boolean} loadingText default 'Loading...'
25664 * Create a new UploadCropbox
25665 * @param {Object} config The config object
25668 Roo.bootstrap.UploadCropbox = function(config){
25669 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
25673 * @event beforeselectfile
25674 * Fire before select file
25675 * @param {Roo.bootstrap.UploadCropbox} this
25677 "beforeselectfile" : true,
25680 * Fire after initEvent
25681 * @param {Roo.bootstrap.UploadCropbox} this
25686 * Fire after initEvent
25687 * @param {Roo.bootstrap.UploadCropbox} this
25688 * @param {String} data
25693 * Fire when preparing the file data
25694 * @param {Roo.bootstrap.UploadCropbox} this
25695 * @param {Object} file
25700 * Fire when get exception
25701 * @param {Roo.bootstrap.UploadCropbox} this
25702 * @param {XMLHttpRequest} xhr
25704 "exception" : true,
25706 * @event beforeloadcanvas
25707 * Fire before load the canvas
25708 * @param {Roo.bootstrap.UploadCropbox} this
25709 * @param {String} src
25711 "beforeloadcanvas" : true,
25714 * Fire when trash image
25715 * @param {Roo.bootstrap.UploadCropbox} this
25720 * Fire when download the image
25721 * @param {Roo.bootstrap.UploadCropbox} this
25725 * @event footerbuttonclick
25726 * Fire when footerbuttonclick
25727 * @param {Roo.bootstrap.UploadCropbox} this
25728 * @param {String} type
25730 "footerbuttonclick" : true,
25734 * @param {Roo.bootstrap.UploadCropbox} this
25739 * Fire when rotate the image
25740 * @param {Roo.bootstrap.UploadCropbox} this
25741 * @param {String} pos
25746 * Fire when inspect the file
25747 * @param {Roo.bootstrap.UploadCropbox} this
25748 * @param {Object} file
25753 * Fire when xhr upload the file
25754 * @param {Roo.bootstrap.UploadCropbox} this
25755 * @param {Object} data
25760 * Fire when arrange the file data
25761 * @param {Roo.bootstrap.UploadCropbox} this
25762 * @param {Object} formData
25767 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
25770 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
25772 emptyText : 'Click to upload image',
25773 rotateNotify : 'Image is too small to rotate',
25774 errorTimeout : 3000,
25788 cropType : 'image/jpeg',
25790 canvasLoaded : false,
25791 isDocument : false,
25793 paramName : 'imageUpload',
25795 loadingText : 'Loading...',
25798 getAutoCreate : function()
25802 cls : 'roo-upload-cropbox',
25806 cls : 'roo-upload-cropbox-selector',
25811 cls : 'roo-upload-cropbox-body',
25812 style : 'cursor:pointer',
25816 cls : 'roo-upload-cropbox-preview'
25820 cls : 'roo-upload-cropbox-thumb'
25824 cls : 'roo-upload-cropbox-empty-notify',
25825 html : this.emptyText
25829 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
25830 html : this.rotateNotify
25836 cls : 'roo-upload-cropbox-footer',
25839 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
25849 onRender : function(ct, position)
25851 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
25853 if (this.buttons.length) {
25855 Roo.each(this.buttons, function(bb) {
25857 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
25859 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
25865 this.maskEl = this.el;
25869 initEvents : function()
25871 this.urlAPI = (window.createObjectURL && window) ||
25872 (window.URL && URL.revokeObjectURL && URL) ||
25873 (window.webkitURL && webkitURL);
25875 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
25876 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25878 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
25879 this.selectorEl.hide();
25881 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
25882 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25884 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
25885 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25886 this.thumbEl.hide();
25888 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
25889 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25891 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
25892 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25893 this.errorEl.hide();
25895 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
25896 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25897 this.footerEl.hide();
25899 this.setThumbBoxSize();
25905 this.fireEvent('initial', this);
25912 window.addEventListener("resize", function() { _this.resize(); } );
25914 this.bodyEl.on('click', this.beforeSelectFile, this);
25917 this.bodyEl.on('touchstart', this.onTouchStart, this);
25918 this.bodyEl.on('touchmove', this.onTouchMove, this);
25919 this.bodyEl.on('touchend', this.onTouchEnd, this);
25923 this.bodyEl.on('mousedown', this.onMouseDown, this);
25924 this.bodyEl.on('mousemove', this.onMouseMove, this);
25925 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
25926 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
25927 Roo.get(document).on('mouseup', this.onMouseUp, this);
25930 this.selectorEl.on('change', this.onFileSelected, this);
25936 this.baseScale = 1;
25938 this.baseRotate = 1;
25939 this.dragable = false;
25940 this.pinching = false;
25943 this.cropData = false;
25944 this.notifyEl.dom.innerHTML = this.emptyText;
25946 this.selectorEl.dom.value = '';
25950 resize : function()
25952 if(this.fireEvent('resize', this) != false){
25953 this.setThumbBoxPosition();
25954 this.setCanvasPosition();
25958 onFooterButtonClick : function(e, el, o, type)
25961 case 'rotate-left' :
25962 this.onRotateLeft(e);
25964 case 'rotate-right' :
25965 this.onRotateRight(e);
25968 this.beforeSelectFile(e);
25983 this.fireEvent('footerbuttonclick', this, type);
25986 beforeSelectFile : function(e)
25988 e.preventDefault();
25990 if(this.fireEvent('beforeselectfile', this) != false){
25991 this.selectorEl.dom.click();
25995 onFileSelected : function(e)
25997 e.preventDefault();
25999 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26003 var file = this.selectorEl.dom.files[0];
26005 if(this.fireEvent('inspect', this, file) != false){
26006 this.prepare(file);
26011 trash : function(e)
26013 this.fireEvent('trash', this);
26016 download : function(e)
26018 this.fireEvent('download', this);
26021 loadCanvas : function(src)
26023 if(this.fireEvent('beforeloadcanvas', this, src) != false){
26027 this.imageEl = document.createElement('img');
26031 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
26033 this.imageEl.src = src;
26037 onLoadCanvas : function()
26039 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
26040 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
26042 this.bodyEl.un('click', this.beforeSelectFile, this);
26044 this.notifyEl.hide();
26045 this.thumbEl.show();
26046 this.footerEl.show();
26048 this.baseRotateLevel();
26050 if(this.isDocument){
26051 this.setThumbBoxSize();
26054 this.setThumbBoxPosition();
26056 this.baseScaleLevel();
26062 this.canvasLoaded = true;
26065 this.maskEl.unmask();
26070 setCanvasPosition : function()
26072 if(!this.canvasEl){
26076 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
26077 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
26079 this.previewEl.setLeft(pw);
26080 this.previewEl.setTop(ph);
26084 onMouseDown : function(e)
26088 this.dragable = true;
26089 this.pinching = false;
26091 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
26092 this.dragable = false;
26096 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26097 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26101 onMouseMove : function(e)
26105 if(!this.canvasLoaded){
26109 if (!this.dragable){
26113 var minX = Math.ceil(this.thumbEl.getLeft(true));
26114 var minY = Math.ceil(this.thumbEl.getTop(true));
26116 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
26117 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
26119 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26120 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26122 x = x - this.mouseX;
26123 y = y - this.mouseY;
26125 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
26126 var bgY = Math.ceil(y + this.previewEl.getTop(true));
26128 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
26129 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
26131 this.previewEl.setLeft(bgX);
26132 this.previewEl.setTop(bgY);
26134 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26135 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26138 onMouseUp : function(e)
26142 this.dragable = false;
26145 onMouseWheel : function(e)
26149 this.startScale = this.scale;
26151 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
26153 if(!this.zoomable()){
26154 this.scale = this.startScale;
26163 zoomable : function()
26165 var minScale = this.thumbEl.getWidth() / this.minWidth;
26167 if(this.minWidth < this.minHeight){
26168 minScale = this.thumbEl.getHeight() / this.minHeight;
26171 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
26172 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
26176 (this.rotate == 0 || this.rotate == 180) &&
26178 width > this.imageEl.OriginWidth ||
26179 height > this.imageEl.OriginHeight ||
26180 (width < this.minWidth && height < this.minHeight)
26188 (this.rotate == 90 || this.rotate == 270) &&
26190 width > this.imageEl.OriginWidth ||
26191 height > this.imageEl.OriginHeight ||
26192 (width < this.minHeight && height < this.minWidth)
26199 !this.isDocument &&
26200 (this.rotate == 0 || this.rotate == 180) &&
26202 width < this.minWidth ||
26203 width > this.imageEl.OriginWidth ||
26204 height < this.minHeight ||
26205 height > this.imageEl.OriginHeight
26212 !this.isDocument &&
26213 (this.rotate == 90 || this.rotate == 270) &&
26215 width < this.minHeight ||
26216 width > this.imageEl.OriginWidth ||
26217 height < this.minWidth ||
26218 height > this.imageEl.OriginHeight
26228 onRotateLeft : function(e)
26230 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
26232 var minScale = this.thumbEl.getWidth() / this.minWidth;
26234 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
26235 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
26237 this.startScale = this.scale;
26239 while (this.getScaleLevel() < minScale){
26241 this.scale = this.scale + 1;
26243 if(!this.zoomable()){
26248 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
26249 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
26254 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
26261 this.scale = this.startScale;
26263 this.onRotateFail();
26268 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
26270 if(this.isDocument){
26271 this.setThumbBoxSize();
26272 this.setThumbBoxPosition();
26273 this.setCanvasPosition();
26278 this.fireEvent('rotate', this, 'left');
26282 onRotateRight : function(e)
26284 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
26286 var minScale = this.thumbEl.getWidth() / this.minWidth;
26288 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
26289 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
26291 this.startScale = this.scale;
26293 while (this.getScaleLevel() < minScale){
26295 this.scale = this.scale + 1;
26297 if(!this.zoomable()){
26302 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
26303 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
26308 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
26315 this.scale = this.startScale;
26317 this.onRotateFail();
26322 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
26324 if(this.isDocument){
26325 this.setThumbBoxSize();
26326 this.setThumbBoxPosition();
26327 this.setCanvasPosition();
26332 this.fireEvent('rotate', this, 'right');
26335 onRotateFail : function()
26337 this.errorEl.show(true);
26341 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
26346 this.previewEl.dom.innerHTML = '';
26348 var canvasEl = document.createElement("canvas");
26350 var contextEl = canvasEl.getContext("2d");
26352 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26353 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26354 var center = this.imageEl.OriginWidth / 2;
26356 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
26357 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26358 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26359 center = this.imageEl.OriginHeight / 2;
26362 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
26364 contextEl.translate(center, center);
26365 contextEl.rotate(this.rotate * Math.PI / 180);
26367 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26369 this.canvasEl = document.createElement("canvas");
26371 this.contextEl = this.canvasEl.getContext("2d");
26373 switch (this.rotate) {
26376 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26377 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26379 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26384 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26385 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26387 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26388 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);
26392 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26397 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26398 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26400 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26401 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);
26405 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);
26410 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26411 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26413 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26414 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26418 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);
26425 this.previewEl.appendChild(this.canvasEl);
26427 this.setCanvasPosition();
26432 if(!this.canvasLoaded){
26436 var imageCanvas = document.createElement("canvas");
26438 var imageContext = imageCanvas.getContext("2d");
26440 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26441 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26443 var center = imageCanvas.width / 2;
26445 imageContext.translate(center, center);
26447 imageContext.rotate(this.rotate * Math.PI / 180);
26449 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26451 var canvas = document.createElement("canvas");
26453 var context = canvas.getContext("2d");
26455 canvas.width = this.minWidth;
26456 canvas.height = this.minHeight;
26458 switch (this.rotate) {
26461 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26462 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26464 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26465 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26467 var targetWidth = this.minWidth - 2 * x;
26468 var targetHeight = this.minHeight - 2 * y;
26472 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26473 scale = targetWidth / width;
26476 if(x > 0 && y == 0){
26477 scale = targetHeight / height;
26480 if(x > 0 && y > 0){
26481 scale = targetWidth / width;
26483 if(width < height){
26484 scale = targetHeight / height;
26488 context.scale(scale, scale);
26490 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26491 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26493 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26494 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26496 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26501 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26502 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26504 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26505 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26507 var targetWidth = this.minWidth - 2 * x;
26508 var targetHeight = this.minHeight - 2 * y;
26512 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26513 scale = targetWidth / width;
26516 if(x > 0 && y == 0){
26517 scale = targetHeight / height;
26520 if(x > 0 && y > 0){
26521 scale = targetWidth / width;
26523 if(width < height){
26524 scale = targetHeight / height;
26528 context.scale(scale, scale);
26530 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26531 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26533 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26534 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26536 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26538 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26543 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26544 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26546 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26547 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26549 var targetWidth = this.minWidth - 2 * x;
26550 var targetHeight = this.minHeight - 2 * y;
26554 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26555 scale = targetWidth / width;
26558 if(x > 0 && y == 0){
26559 scale = targetHeight / height;
26562 if(x > 0 && y > 0){
26563 scale = targetWidth / width;
26565 if(width < height){
26566 scale = targetHeight / height;
26570 context.scale(scale, scale);
26572 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26573 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26575 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26576 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26578 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26579 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26581 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26586 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26587 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26589 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26590 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26592 var targetWidth = this.minWidth - 2 * x;
26593 var targetHeight = this.minHeight - 2 * y;
26597 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26598 scale = targetWidth / width;
26601 if(x > 0 && y == 0){
26602 scale = targetHeight / height;
26605 if(x > 0 && y > 0){
26606 scale = targetWidth / width;
26608 if(width < height){
26609 scale = targetHeight / height;
26613 context.scale(scale, scale);
26615 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26616 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26618 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26619 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26621 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26623 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26630 this.cropData = canvas.toDataURL(this.cropType);
26632 if(this.fireEvent('crop', this, this.cropData) !== false){
26633 this.process(this.file, this.cropData);
26640 setThumbBoxSize : function()
26644 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
26645 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
26646 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
26648 this.minWidth = width;
26649 this.minHeight = height;
26651 if(this.rotate == 90 || this.rotate == 270){
26652 this.minWidth = height;
26653 this.minHeight = width;
26658 width = Math.ceil(this.minWidth * height / this.minHeight);
26660 if(this.minWidth > this.minHeight){
26662 height = Math.ceil(this.minHeight * width / this.minWidth);
26665 this.thumbEl.setStyle({
26666 width : width + 'px',
26667 height : height + 'px'
26674 setThumbBoxPosition : function()
26676 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
26677 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
26679 this.thumbEl.setLeft(x);
26680 this.thumbEl.setTop(y);
26684 baseRotateLevel : function()
26686 this.baseRotate = 1;
26689 typeof(this.exif) != 'undefined' &&
26690 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
26691 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
26693 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
26696 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
26700 baseScaleLevel : function()
26704 if(this.isDocument){
26706 if(this.baseRotate == 6 || this.baseRotate == 8){
26708 height = this.thumbEl.getHeight();
26709 this.baseScale = height / this.imageEl.OriginWidth;
26711 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
26712 width = this.thumbEl.getWidth();
26713 this.baseScale = width / this.imageEl.OriginHeight;
26719 height = this.thumbEl.getHeight();
26720 this.baseScale = height / this.imageEl.OriginHeight;
26722 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
26723 width = this.thumbEl.getWidth();
26724 this.baseScale = width / this.imageEl.OriginWidth;
26730 if(this.baseRotate == 6 || this.baseRotate == 8){
26732 width = this.thumbEl.getHeight();
26733 this.baseScale = width / this.imageEl.OriginHeight;
26735 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
26736 height = this.thumbEl.getWidth();
26737 this.baseScale = height / this.imageEl.OriginHeight;
26740 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26741 height = this.thumbEl.getWidth();
26742 this.baseScale = height / this.imageEl.OriginHeight;
26744 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
26745 width = this.thumbEl.getHeight();
26746 this.baseScale = width / this.imageEl.OriginWidth;
26753 width = this.thumbEl.getWidth();
26754 this.baseScale = width / this.imageEl.OriginWidth;
26756 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
26757 height = this.thumbEl.getHeight();
26758 this.baseScale = height / this.imageEl.OriginHeight;
26761 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26763 height = this.thumbEl.getHeight();
26764 this.baseScale = height / this.imageEl.OriginHeight;
26766 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
26767 width = this.thumbEl.getWidth();
26768 this.baseScale = width / this.imageEl.OriginWidth;
26776 getScaleLevel : function()
26778 return this.baseScale * Math.pow(1.1, this.scale);
26781 onTouchStart : function(e)
26783 if(!this.canvasLoaded){
26784 this.beforeSelectFile(e);
26788 var touches = e.browserEvent.touches;
26794 if(touches.length == 1){
26795 this.onMouseDown(e);
26799 if(touches.length != 2){
26805 for(var i = 0, finger; finger = touches[i]; i++){
26806 coords.push(finger.pageX, finger.pageY);
26809 var x = Math.pow(coords[0] - coords[2], 2);
26810 var y = Math.pow(coords[1] - coords[3], 2);
26812 this.startDistance = Math.sqrt(x + y);
26814 this.startScale = this.scale;
26816 this.pinching = true;
26817 this.dragable = false;
26821 onTouchMove : function(e)
26823 if(!this.pinching && !this.dragable){
26827 var touches = e.browserEvent.touches;
26834 this.onMouseMove(e);
26840 for(var i = 0, finger; finger = touches[i]; i++){
26841 coords.push(finger.pageX, finger.pageY);
26844 var x = Math.pow(coords[0] - coords[2], 2);
26845 var y = Math.pow(coords[1] - coords[3], 2);
26847 this.endDistance = Math.sqrt(x + y);
26849 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
26851 if(!this.zoomable()){
26852 this.scale = this.startScale;
26860 onTouchEnd : function(e)
26862 this.pinching = false;
26863 this.dragable = false;
26867 process : function(file, crop)
26870 this.maskEl.mask(this.loadingText);
26873 this.xhr = new XMLHttpRequest();
26875 file.xhr = this.xhr;
26877 this.xhr.open(this.method, this.url, true);
26880 "Accept": "application/json",
26881 "Cache-Control": "no-cache",
26882 "X-Requested-With": "XMLHttpRequest"
26885 for (var headerName in headers) {
26886 var headerValue = headers[headerName];
26888 this.xhr.setRequestHeader(headerName, headerValue);
26894 this.xhr.onload = function()
26896 _this.xhrOnLoad(_this.xhr);
26899 this.xhr.onerror = function()
26901 _this.xhrOnError(_this.xhr);
26904 var formData = new FormData();
26906 formData.append('returnHTML', 'NO');
26909 formData.append('crop', crop);
26912 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
26913 formData.append(this.paramName, file, file.name);
26916 if(typeof(file.filename) != 'undefined'){
26917 formData.append('filename', file.filename);
26920 if(typeof(file.mimetype) != 'undefined'){
26921 formData.append('mimetype', file.mimetype);
26924 if(this.fireEvent('arrange', this, formData) != false){
26925 this.xhr.send(formData);
26929 xhrOnLoad : function(xhr)
26932 this.maskEl.unmask();
26935 if (xhr.readyState !== 4) {
26936 this.fireEvent('exception', this, xhr);
26940 var response = Roo.decode(xhr.responseText);
26942 if(!response.success){
26943 this.fireEvent('exception', this, xhr);
26947 var response = Roo.decode(xhr.responseText);
26949 this.fireEvent('upload', this, response);
26953 xhrOnError : function()
26956 this.maskEl.unmask();
26959 Roo.log('xhr on error');
26961 var response = Roo.decode(xhr.responseText);
26967 prepare : function(file)
26970 this.maskEl.mask(this.loadingText);
26976 if(typeof(file) === 'string'){
26977 this.loadCanvas(file);
26981 if(!file || !this.urlAPI){
26986 this.cropType = file.type;
26990 if(this.fireEvent('prepare', this, this.file) != false){
26992 var reader = new FileReader();
26994 reader.onload = function (e) {
26995 if (e.target.error) {
26996 Roo.log(e.target.error);
27000 var buffer = e.target.result,
27001 dataView = new DataView(buffer),
27003 maxOffset = dataView.byteLength - 4,
27007 if (dataView.getUint16(0) === 0xffd8) {
27008 while (offset < maxOffset) {
27009 markerBytes = dataView.getUint16(offset);
27011 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
27012 markerLength = dataView.getUint16(offset + 2) + 2;
27013 if (offset + markerLength > dataView.byteLength) {
27014 Roo.log('Invalid meta data: Invalid segment size.');
27018 if(markerBytes == 0xffe1){
27019 _this.parseExifData(
27026 offset += markerLength;
27036 var url = _this.urlAPI.createObjectURL(_this.file);
27038 _this.loadCanvas(url);
27043 reader.readAsArrayBuffer(this.file);
27049 parseExifData : function(dataView, offset, length)
27051 var tiffOffset = offset + 10,
27055 if (dataView.getUint32(offset + 4) !== 0x45786966) {
27056 // No Exif data, might be XMP data instead
27060 // Check for the ASCII code for "Exif" (0x45786966):
27061 if (dataView.getUint32(offset + 4) !== 0x45786966) {
27062 // No Exif data, might be XMP data instead
27065 if (tiffOffset + 8 > dataView.byteLength) {
27066 Roo.log('Invalid Exif data: Invalid segment size.');
27069 // Check for the two null bytes:
27070 if (dataView.getUint16(offset + 8) !== 0x0000) {
27071 Roo.log('Invalid Exif data: Missing byte alignment offset.');
27074 // Check the byte alignment:
27075 switch (dataView.getUint16(tiffOffset)) {
27077 littleEndian = true;
27080 littleEndian = false;
27083 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
27086 // Check for the TIFF tag marker (0x002A):
27087 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
27088 Roo.log('Invalid Exif data: Missing TIFF marker.');
27091 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
27092 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
27094 this.parseExifTags(
27097 tiffOffset + dirOffset,
27102 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
27107 if (dirOffset + 6 > dataView.byteLength) {
27108 Roo.log('Invalid Exif data: Invalid directory offset.');
27111 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
27112 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
27113 if (dirEndOffset + 4 > dataView.byteLength) {
27114 Roo.log('Invalid Exif data: Invalid directory size.');
27117 for (i = 0; i < tagsNumber; i += 1) {
27121 dirOffset + 2 + 12 * i, // tag offset
27125 // Return the offset to the next directory:
27126 return dataView.getUint32(dirEndOffset, littleEndian);
27129 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
27131 var tag = dataView.getUint16(offset, littleEndian);
27133 this.exif[tag] = this.getExifValue(
27137 dataView.getUint16(offset + 2, littleEndian), // tag type
27138 dataView.getUint32(offset + 4, littleEndian), // tag length
27143 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
27145 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
27154 Roo.log('Invalid Exif data: Invalid tag type.');
27158 tagSize = tagType.size * length;
27159 // Determine if the value is contained in the dataOffset bytes,
27160 // or if the value at the dataOffset is a pointer to the actual data:
27161 dataOffset = tagSize > 4 ?
27162 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
27163 if (dataOffset + tagSize > dataView.byteLength) {
27164 Roo.log('Invalid Exif data: Invalid data offset.');
27167 if (length === 1) {
27168 return tagType.getValue(dataView, dataOffset, littleEndian);
27171 for (i = 0; i < length; i += 1) {
27172 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
27175 if (tagType.ascii) {
27177 // Concatenate the chars:
27178 for (i = 0; i < values.length; i += 1) {
27180 // Ignore the terminating NULL byte(s):
27181 if (c === '\u0000') {
27193 Roo.apply(Roo.bootstrap.UploadCropbox, {
27195 'Orientation': 0x0112
27199 1: 0, //'top-left',
27201 3: 180, //'bottom-right',
27202 // 4: 'bottom-left',
27204 6: 90, //'right-top',
27205 // 7: 'right-bottom',
27206 8: 270 //'left-bottom'
27210 // byte, 8-bit unsigned int:
27212 getValue: function (dataView, dataOffset) {
27213 return dataView.getUint8(dataOffset);
27217 // ascii, 8-bit byte:
27219 getValue: function (dataView, dataOffset) {
27220 return String.fromCharCode(dataView.getUint8(dataOffset));
27225 // short, 16 bit int:
27227 getValue: function (dataView, dataOffset, littleEndian) {
27228 return dataView.getUint16(dataOffset, littleEndian);
27232 // long, 32 bit int:
27234 getValue: function (dataView, dataOffset, littleEndian) {
27235 return dataView.getUint32(dataOffset, littleEndian);
27239 // rational = two long values, first is numerator, second is denominator:
27241 getValue: function (dataView, dataOffset, littleEndian) {
27242 return dataView.getUint32(dataOffset, littleEndian) /
27243 dataView.getUint32(dataOffset + 4, littleEndian);
27247 // slong, 32 bit signed int:
27249 getValue: function (dataView, dataOffset, littleEndian) {
27250 return dataView.getInt32(dataOffset, littleEndian);
27254 // srational, two slongs, first is numerator, second is denominator:
27256 getValue: function (dataView, dataOffset, littleEndian) {
27257 return dataView.getInt32(dataOffset, littleEndian) /
27258 dataView.getInt32(dataOffset + 4, littleEndian);
27268 cls : 'btn-group roo-upload-cropbox-rotate-left',
27269 action : 'rotate-left',
27273 cls : 'btn btn-default',
27274 html : '<i class="fa fa-undo"></i>'
27280 cls : 'btn-group roo-upload-cropbox-picture',
27281 action : 'picture',
27285 cls : 'btn btn-default',
27286 html : '<i class="fa fa-picture-o"></i>'
27292 cls : 'btn-group roo-upload-cropbox-rotate-right',
27293 action : 'rotate-right',
27297 cls : 'btn btn-default',
27298 html : '<i class="fa fa-repeat"></i>'
27306 cls : 'btn-group roo-upload-cropbox-rotate-left',
27307 action : 'rotate-left',
27311 cls : 'btn btn-default',
27312 html : '<i class="fa fa-undo"></i>'
27318 cls : 'btn-group roo-upload-cropbox-download',
27319 action : 'download',
27323 cls : 'btn btn-default',
27324 html : '<i class="fa fa-download"></i>'
27330 cls : 'btn-group roo-upload-cropbox-crop',
27335 cls : 'btn btn-default',
27336 html : '<i class="fa fa-crop"></i>'
27342 cls : 'btn-group roo-upload-cropbox-trash',
27347 cls : 'btn btn-default',
27348 html : '<i class="fa fa-trash"></i>'
27354 cls : 'btn-group roo-upload-cropbox-rotate-right',
27355 action : 'rotate-right',
27359 cls : 'btn btn-default',
27360 html : '<i class="fa fa-repeat"></i>'
27368 cls : 'btn-group roo-upload-cropbox-rotate-left',
27369 action : 'rotate-left',
27373 cls : 'btn btn-default',
27374 html : '<i class="fa fa-undo"></i>'
27380 cls : 'btn-group roo-upload-cropbox-rotate-right',
27381 action : 'rotate-right',
27385 cls : 'btn btn-default',
27386 html : '<i class="fa fa-repeat"></i>'
27399 * @class Roo.bootstrap.DocumentManager
27400 * @extends Roo.bootstrap.Component
27401 * Bootstrap DocumentManager class
27402 * @cfg {String} paramName default 'imageUpload'
27403 * @cfg {String} toolTipName default 'filename'
27404 * @cfg {String} method default POST
27405 * @cfg {String} url action url
27406 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
27407 * @cfg {Boolean} multiple multiple upload default true
27408 * @cfg {Number} thumbSize default 300
27409 * @cfg {String} fieldLabel
27410 * @cfg {Number} labelWidth default 4
27411 * @cfg {String} labelAlign (left|top) default left
27412 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
27415 * Create a new DocumentManager
27416 * @param {Object} config The config object
27419 Roo.bootstrap.DocumentManager = function(config){
27420 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
27423 this.delegates = [];
27428 * Fire when initial the DocumentManager
27429 * @param {Roo.bootstrap.DocumentManager} this
27434 * inspect selected file
27435 * @param {Roo.bootstrap.DocumentManager} this
27436 * @param {File} file
27441 * Fire when xhr load exception
27442 * @param {Roo.bootstrap.DocumentManager} this
27443 * @param {XMLHttpRequest} xhr
27445 "exception" : true,
27447 * @event afterupload
27448 * Fire when xhr load exception
27449 * @param {Roo.bootstrap.DocumentManager} this
27450 * @param {XMLHttpRequest} xhr
27452 "afterupload" : true,
27455 * prepare the form data
27456 * @param {Roo.bootstrap.DocumentManager} this
27457 * @param {Object} formData
27462 * Fire when remove the file
27463 * @param {Roo.bootstrap.DocumentManager} this
27464 * @param {Object} file
27469 * Fire after refresh the file
27470 * @param {Roo.bootstrap.DocumentManager} this
27475 * Fire after click the image
27476 * @param {Roo.bootstrap.DocumentManager} this
27477 * @param {Object} file
27482 * Fire when upload a image and editable set to true
27483 * @param {Roo.bootstrap.DocumentManager} this
27484 * @param {Object} file
27488 * @event beforeselectfile
27489 * Fire before select file
27490 * @param {Roo.bootstrap.DocumentManager} this
27492 "beforeselectfile" : true,
27495 * Fire before process file
27496 * @param {Roo.bootstrap.DocumentManager} this
27497 * @param {Object} file
27504 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
27513 paramName : 'imageUpload',
27514 toolTipName : 'filename',
27517 labelAlign : 'left',
27522 getAutoCreate : function()
27524 var managerWidget = {
27526 cls : 'roo-document-manager',
27530 cls : 'roo-document-manager-selector',
27535 cls : 'roo-document-manager-uploader',
27539 cls : 'roo-document-manager-upload-btn',
27540 html : '<i class="fa fa-plus"></i>'
27551 cls : 'column col-md-12',
27556 if(this.fieldLabel.length){
27561 cls : 'column col-md-12',
27562 html : this.fieldLabel
27566 cls : 'column col-md-12',
27571 if(this.labelAlign == 'left'){
27575 cls : 'column col-md-' + this.labelWidth,
27576 html : this.fieldLabel
27580 cls : 'column col-md-' + (12 - this.labelWidth),
27590 cls : 'row clearfix',
27598 initEvents : function()
27600 this.managerEl = this.el.select('.roo-document-manager', true).first();
27601 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27603 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
27604 this.selectorEl.hide();
27607 this.selectorEl.attr('multiple', 'multiple');
27610 this.selectorEl.on('change', this.onFileSelected, this);
27612 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
27613 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27615 this.uploader.on('click', this.onUploaderClick, this);
27617 this.renderProgressDialog();
27621 window.addEventListener("resize", function() { _this.refresh(); } );
27623 this.fireEvent('initial', this);
27626 renderProgressDialog : function()
27630 this.progressDialog = new Roo.bootstrap.Modal({
27631 cls : 'roo-document-manager-progress-dialog',
27632 allow_close : false,
27642 btnclick : function() {
27643 _this.uploadCancel();
27649 this.progressDialog.render(Roo.get(document.body));
27651 this.progress = new Roo.bootstrap.Progress({
27652 cls : 'roo-document-manager-progress',
27657 this.progress.render(this.progressDialog.getChildContainer());
27659 this.progressBar = new Roo.bootstrap.ProgressBar({
27660 cls : 'roo-document-manager-progress-bar',
27663 aria_valuemax : 12,
27667 this.progressBar.render(this.progress.getChildContainer());
27670 onUploaderClick : function(e)
27672 e.preventDefault();
27674 if(this.fireEvent('beforeselectfile', this) != false){
27675 this.selectorEl.dom.click();
27680 onFileSelected : function(e)
27682 e.preventDefault();
27684 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27688 Roo.each(this.selectorEl.dom.files, function(file){
27689 if(this.fireEvent('inspect', this, file) != false){
27690 this.files.push(file);
27700 this.selectorEl.dom.value = '';
27702 if(!this.files.length){
27706 if(this.boxes > 0 && this.files.length > this.boxes){
27707 this.files = this.files.slice(0, this.boxes);
27710 this.uploader.show();
27712 if(this.boxes > 0 && this.files.length > this.boxes - 1){
27713 this.uploader.hide();
27722 Roo.each(this.files, function(file){
27724 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27725 var f = this.renderPreview(file);
27730 if(file.type.indexOf('image') != -1){
27731 this.delegates.push(
27733 _this.process(file);
27734 }).createDelegate(this)
27742 _this.process(file);
27743 }).createDelegate(this)
27748 this.files = files;
27750 this.delegates = this.delegates.concat(docs);
27752 if(!this.delegates.length){
27757 this.progressBar.aria_valuemax = this.delegates.length;
27764 arrange : function()
27766 if(!this.delegates.length){
27767 this.progressDialog.hide();
27772 var delegate = this.delegates.shift();
27774 this.progressDialog.show();
27776 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
27778 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
27783 refresh : function()
27785 this.uploader.show();
27787 if(this.boxes > 0 && this.files.length > this.boxes - 1){
27788 this.uploader.hide();
27791 Roo.isTouch ? this.closable(false) : this.closable(true);
27793 this.fireEvent('refresh', this);
27796 onRemove : function(e, el, o)
27798 e.preventDefault();
27800 this.fireEvent('remove', this, o);
27804 remove : function(o)
27808 Roo.each(this.files, function(file){
27809 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
27818 this.files = files;
27825 Roo.each(this.files, function(file){
27830 file.target.remove();
27839 onClick : function(e, el, o)
27841 e.preventDefault();
27843 this.fireEvent('click', this, o);
27847 closable : function(closable)
27849 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
27851 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27863 xhrOnLoad : function(xhr)
27865 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27869 if (xhr.readyState !== 4) {
27871 this.fireEvent('exception', this, xhr);
27875 var response = Roo.decode(xhr.responseText);
27877 if(!response.success){
27879 this.fireEvent('exception', this, xhr);
27883 var file = this.renderPreview(response.data);
27885 this.files.push(file);
27889 this.fireEvent('afterupload', this, xhr);
27893 xhrOnError : function(xhr)
27895 Roo.log('xhr on error');
27897 var response = Roo.decode(xhr.responseText);
27904 process : function(file)
27906 if(this.fireEvent('process', this, file) !== false){
27907 if(this.editable && file.type.indexOf('image') != -1){
27908 this.fireEvent('edit', this, file);
27912 this.uploadStart(file, false);
27919 uploadStart : function(file, crop)
27921 this.xhr = new XMLHttpRequest();
27923 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27928 file.xhr = this.xhr;
27930 this.managerEl.createChild({
27932 cls : 'roo-document-manager-loading',
27936 tooltip : file.name,
27937 cls : 'roo-document-manager-thumb',
27938 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27944 this.xhr.open(this.method, this.url, true);
27947 "Accept": "application/json",
27948 "Cache-Control": "no-cache",
27949 "X-Requested-With": "XMLHttpRequest"
27952 for (var headerName in headers) {
27953 var headerValue = headers[headerName];
27955 this.xhr.setRequestHeader(headerName, headerValue);
27961 this.xhr.onload = function()
27963 _this.xhrOnLoad(_this.xhr);
27966 this.xhr.onerror = function()
27968 _this.xhrOnError(_this.xhr);
27971 var formData = new FormData();
27973 formData.append('returnHTML', 'NO');
27976 formData.append('crop', crop);
27979 formData.append(this.paramName, file, file.name);
27986 if(this.fireEvent('prepare', this, formData, options) != false){
27988 if(options.manually){
27992 this.xhr.send(formData);
27996 this.uploadCancel();
27999 uploadCancel : function()
28005 this.delegates = [];
28007 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28014 renderPreview : function(file)
28016 if(typeof(file.target) != 'undefined' && file.target){
28020 var previewEl = this.managerEl.createChild({
28022 cls : 'roo-document-manager-preview',
28026 tooltip : file[this.toolTipName],
28027 cls : 'roo-document-manager-thumb',
28028 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
28033 html : '<i class="fa fa-times-circle"></i>'
28038 var close = previewEl.select('button.close', true).first();
28040 close.on('click', this.onRemove, this, file);
28042 file.target = previewEl;
28044 var image = previewEl.select('img', true).first();
28048 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
28050 image.on('click', this.onClick, this, file);
28056 onPreviewLoad : function(file, image)
28058 if(typeof(file.target) == 'undefined' || !file.target){
28062 var width = image.dom.naturalWidth || image.dom.width;
28063 var height = image.dom.naturalHeight || image.dom.height;
28065 if(width > height){
28066 file.target.addClass('wide');
28070 file.target.addClass('tall');
28075 uploadFromSource : function(file, crop)
28077 this.xhr = new XMLHttpRequest();
28079 this.managerEl.createChild({
28081 cls : 'roo-document-manager-loading',
28085 tooltip : file.name,
28086 cls : 'roo-document-manager-thumb',
28087 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28093 this.xhr.open(this.method, this.url, true);
28096 "Accept": "application/json",
28097 "Cache-Control": "no-cache",
28098 "X-Requested-With": "XMLHttpRequest"
28101 for (var headerName in headers) {
28102 var headerValue = headers[headerName];
28104 this.xhr.setRequestHeader(headerName, headerValue);
28110 this.xhr.onload = function()
28112 _this.xhrOnLoad(_this.xhr);
28115 this.xhr.onerror = function()
28117 _this.xhrOnError(_this.xhr);
28120 var formData = new FormData();
28122 formData.append('returnHTML', 'NO');
28124 formData.append('crop', crop);
28126 if(typeof(file.filename) != 'undefined'){
28127 formData.append('filename', file.filename);
28130 if(typeof(file.mimetype) != 'undefined'){
28131 formData.append('mimetype', file.mimetype);
28134 if(this.fireEvent('prepare', this, formData) != false){
28135 this.xhr.send(formData);
28145 * @class Roo.bootstrap.DocumentViewer
28146 * @extends Roo.bootstrap.Component
28147 * Bootstrap DocumentViewer class
28148 * @cfg {Boolean} showDownload (true|false) show download button (default true)
28149 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
28152 * Create a new DocumentViewer
28153 * @param {Object} config The config object
28156 Roo.bootstrap.DocumentViewer = function(config){
28157 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
28162 * Fire after initEvent
28163 * @param {Roo.bootstrap.DocumentViewer} this
28169 * @param {Roo.bootstrap.DocumentViewer} this
28174 * Fire after download button
28175 * @param {Roo.bootstrap.DocumentViewer} this
28180 * Fire after trash button
28181 * @param {Roo.bootstrap.DocumentViewer} this
28188 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
28190 showDownload : true,
28194 getAutoCreate : function()
28198 cls : 'roo-document-viewer',
28202 cls : 'roo-document-viewer-body',
28206 cls : 'roo-document-viewer-thumb',
28210 cls : 'roo-document-viewer-image'
28218 cls : 'roo-document-viewer-footer',
28221 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
28225 cls : 'btn-group roo-document-viewer-download',
28229 cls : 'btn btn-default',
28230 html : '<i class="fa fa-download"></i>'
28236 cls : 'btn-group roo-document-viewer-trash',
28240 cls : 'btn btn-default',
28241 html : '<i class="fa fa-trash"></i>'
28254 initEvents : function()
28257 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
28258 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
28260 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
28261 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
28263 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
28264 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
28266 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
28267 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
28269 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
28270 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
28272 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
28273 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
28275 this.bodyEl.on('click', this.onClick, this);
28276 this.downloadBtn.on('click', this.onDownload, this);
28277 this.trashBtn.on('click', this.onTrash, this);
28279 this.downloadBtn.hide();
28280 this.trashBtn.hide();
28282 if(this.showDownload){
28283 this.downloadBtn.show();
28286 if(this.showTrash){
28287 this.trashBtn.show();
28290 if(!this.showDownload && !this.showTrash) {
28291 this.footerEl.hide();
28296 initial : function()
28298 // this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
28301 this.fireEvent('initial', this);
28305 onClick : function(e)
28307 e.preventDefault();
28309 this.fireEvent('click', this);
28312 onDownload : function(e)
28314 e.preventDefault();
28316 this.fireEvent('download', this);
28319 onTrash : function(e)
28321 e.preventDefault();
28323 this.fireEvent('trash', this);
28335 * @class Roo.bootstrap.NavProgressBar
28336 * @extends Roo.bootstrap.Component
28337 * Bootstrap NavProgressBar class
28340 * Create a new nav progress bar
28341 * @param {Object} config The config object
28344 Roo.bootstrap.NavProgressBar = function(config){
28345 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
28347 this.bullets = this.bullets || [];
28349 // Roo.bootstrap.NavProgressBar.register(this);
28353 * Fires when the active item changes
28354 * @param {Roo.bootstrap.NavProgressBar} this
28355 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
28356 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
28363 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
28368 getAutoCreate : function()
28370 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
28374 cls : 'roo-navigation-bar-group',
28378 cls : 'roo-navigation-top-bar'
28382 cls : 'roo-navigation-bullets-bar',
28386 cls : 'roo-navigation-bar'
28393 cls : 'roo-navigation-bottom-bar'
28403 initEvents: function()
28408 onRender : function(ct, position)
28410 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28412 if(this.bullets.length){
28413 Roo.each(this.bullets, function(b){
28422 addItem : function(cfg)
28424 var item = new Roo.bootstrap.NavProgressItem(cfg);
28426 item.parentId = this.id;
28427 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
28430 var top = new Roo.bootstrap.Element({
28432 cls : 'roo-navigation-bar-text'
28435 var bottom = new Roo.bootstrap.Element({
28437 cls : 'roo-navigation-bar-text'
28440 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
28441 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
28443 var topText = new Roo.bootstrap.Element({
28445 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
28448 var bottomText = new Roo.bootstrap.Element({
28450 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
28453 topText.onRender(top.el, null);
28454 bottomText.onRender(bottom.el, null);
28457 item.bottomEl = bottom;
28460 this.barItems.push(item);
28465 getActive : function()
28467 var active = false;
28469 Roo.each(this.barItems, function(v){
28471 if (!v.isActive()) {
28483 setActiveItem : function(item)
28487 Roo.each(this.barItems, function(v){
28488 if (v.rid == item.rid) {
28492 if (v.isActive()) {
28493 v.setActive(false);
28498 item.setActive(true);
28500 this.fireEvent('changed', this, item, prev);
28503 getBarItem: function(rid)
28507 Roo.each(this.barItems, function(e) {
28508 if (e.rid != rid) {
28519 indexOfItem : function(item)
28523 Roo.each(this.barItems, function(v, i){
28525 if (v.rid != item.rid) {
28536 setActiveNext : function()
28538 var i = this.indexOfItem(this.getActive());
28540 if (i > this.barItems.length) {
28544 this.setActiveItem(this.barItems[i+1]);
28547 setActivePrev : function()
28549 var i = this.indexOfItem(this.getActive());
28555 this.setActiveItem(this.barItems[i-1]);
28558 format : function()
28560 if(!this.barItems.length){
28564 var width = 100 / this.barItems.length;
28566 Roo.each(this.barItems, function(i){
28567 i.el.setStyle('width', width + '%');
28568 i.topEl.el.setStyle('width', width + '%');
28569 i.bottomEl.el.setStyle('width', width + '%');
28578 * Nav Progress Item
28583 * @class Roo.bootstrap.NavProgressItem
28584 * @extends Roo.bootstrap.Component
28585 * Bootstrap NavProgressItem class
28586 * @cfg {String} rid the reference id
28587 * @cfg {Boolean} active (true|false) Is item active default false
28588 * @cfg {Boolean} disabled (true|false) Is item active default false
28589 * @cfg {String} html
28590 * @cfg {String} position (top|bottom) text position default bottom
28591 * @cfg {String} icon show icon instead of number
28594 * Create a new NavProgressItem
28595 * @param {Object} config The config object
28597 Roo.bootstrap.NavProgressItem = function(config){
28598 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
28603 * The raw click event for the entire grid.
28604 * @param {Roo.bootstrap.NavProgressItem} this
28605 * @param {Roo.EventObject} e
28612 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
28618 position : 'bottom',
28621 getAutoCreate : function()
28623 var iconCls = 'roo-navigation-bar-item-icon';
28625 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
28629 cls: 'roo-navigation-bar-item',
28639 cfg.cls += ' active';
28642 cfg.cls += ' disabled';
28648 disable : function()
28650 this.setDisabled(true);
28653 enable : function()
28655 this.setDisabled(false);
28658 initEvents: function()
28660 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
28662 this.iconEl.on('click', this.onClick, this);
28665 onClick : function(e)
28667 e.preventDefault();
28673 if(this.fireEvent('click', this, e) === false){
28677 this.parent().setActiveItem(this);
28680 isActive: function ()
28682 return this.active;
28685 setActive : function(state)
28687 if(this.active == state){
28691 this.active = state;
28694 this.el.addClass('active');
28698 this.el.removeClass('active');
28703 setDisabled : function(state)
28705 if(this.disabled == state){
28709 this.disabled = state;
28712 this.el.addClass('disabled');
28716 this.el.removeClass('disabled');
28719 tooltipEl : function()
28721 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
28734 * @class Roo.bootstrap.FieldLabel
28735 * @extends Roo.bootstrap.Component
28736 * Bootstrap FieldLabel class
28737 * @cfg {String} html contents of the element
28738 * @cfg {String} tag tag of the element default label
28739 * @cfg {String} cls class of the element
28740 * @cfg {String} target label target
28741 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
28742 * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
28743 * @cfg {String} validClass default "text-success fa fa-lg fa-check"
28744 * @cfg {String} iconTooltip default "This field is required"
28747 * Create a new FieldLabel
28748 * @param {Object} config The config object
28751 Roo.bootstrap.FieldLabel = function(config){
28752 Roo.bootstrap.Element.superclass.constructor.call(this, config);
28757 * Fires after the field has been marked as invalid.
28758 * @param {Roo.form.FieldLabel} this
28759 * @param {String} msg The validation message
28764 * Fires after the field has been validated with no errors.
28765 * @param {Roo.form.FieldLabel} this
28771 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
28778 invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
28779 validClass : 'text-success fa fa-lg fa-check',
28780 iconTooltip : 'This field is required',
28782 getAutoCreate : function(){
28786 cls : 'roo-bootstrap-field-label ' + this.cls,
28792 tooltip : this.iconTooltip
28804 initEvents: function()
28806 Roo.bootstrap.Element.superclass.initEvents.call(this);
28808 this.iconEl = this.el.select('i', true).first();
28810 this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
28812 Roo.bootstrap.FieldLabel.register(this);
28816 * Mark this field as valid
28818 markValid : function()
28820 this.iconEl.show();
28822 this.iconEl.removeClass(this.invalidClass);
28824 this.iconEl.addClass(this.validClass);
28826 this.fireEvent('valid', this);
28830 * Mark this field as invalid
28831 * @param {String} msg The validation message
28833 markInvalid : function(msg)
28835 this.iconEl.show();
28837 this.iconEl.removeClass(this.validClass);
28839 this.iconEl.addClass(this.invalidClass);
28841 this.fireEvent('invalid', this, msg);
28847 Roo.apply(Roo.bootstrap.FieldLabel, {
28852 * register a FieldLabel Group
28853 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
28855 register : function(label)
28857 if(this.groups.hasOwnProperty(label.target)){
28861 this.groups[label.target] = label;
28865 * fetch a FieldLabel Group based on the target
28866 * @param {string} target
28867 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
28869 get: function(target) {
28870 if (typeof(this.groups[target]) == 'undefined') {
28874 return this.groups[target] ;
28883 * page DateSplitField.
28889 * @class Roo.bootstrap.DateSplitField
28890 * @extends Roo.bootstrap.Component
28891 * Bootstrap DateSplitField class
28892 * @cfg {string} fieldLabel - the label associated
28893 * @cfg {Number} labelWidth set the width of label (0-12)
28894 * @cfg {String} labelAlign (top|left)
28895 * @cfg {Boolean} dayAllowBlank (true|false) default false
28896 * @cfg {Boolean} monthAllowBlank (true|false) default false
28897 * @cfg {Boolean} yearAllowBlank (true|false) default false
28898 * @cfg {string} dayPlaceholder
28899 * @cfg {string} monthPlaceholder
28900 * @cfg {string} yearPlaceholder
28901 * @cfg {string} dayFormat default 'd'
28902 * @cfg {string} monthFormat default 'm'
28903 * @cfg {string} yearFormat default 'Y'
28907 * Create a new DateSplitField
28908 * @param {Object} config The config object
28911 Roo.bootstrap.DateSplitField = function(config){
28912 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
28918 * getting the data of years
28919 * @param {Roo.bootstrap.DateSplitField} this
28920 * @param {Object} years
28925 * getting the data of days
28926 * @param {Roo.bootstrap.DateSplitField} this
28927 * @param {Object} days
28932 * Fires after the field has been marked as invalid.
28933 * @param {Roo.form.Field} this
28934 * @param {String} msg The validation message
28939 * Fires after the field has been validated with no errors.
28940 * @param {Roo.form.Field} this
28946 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
28949 labelAlign : 'top',
28951 dayAllowBlank : false,
28952 monthAllowBlank : false,
28953 yearAllowBlank : false,
28954 dayPlaceholder : '',
28955 monthPlaceholder : '',
28956 yearPlaceholder : '',
28960 isFormField : true,
28962 getAutoCreate : function()
28966 cls : 'row roo-date-split-field-group',
28971 cls : 'form-hidden-field roo-date-split-field-group-value',
28977 if(this.fieldLabel){
28980 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
28984 html : this.fieldLabel
28990 Roo.each(['day', 'month', 'year'], function(t){
28993 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
29000 inputEl: function ()
29002 return this.el.select('.roo-date-split-field-group-value', true).first();
29005 onRender : function(ct, position)
29009 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29011 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
29013 this.dayField = new Roo.bootstrap.ComboBox({
29014 allowBlank : this.dayAllowBlank,
29015 alwaysQuery : true,
29016 displayField : 'value',
29019 forceSelection : true,
29021 placeholder : this.dayPlaceholder,
29022 selectOnFocus : true,
29023 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29024 triggerAction : 'all',
29026 valueField : 'value',
29027 store : new Roo.data.SimpleStore({
29028 data : (function() {
29030 _this.fireEvent('days', _this, days);
29033 fields : [ 'value' ]
29036 select : function (_self, record, index)
29038 _this.setValue(_this.getValue());
29043 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
29045 this.monthField = new Roo.bootstrap.MonthField({
29046 after : '<i class=\"fa fa-calendar\"></i>',
29047 allowBlank : this.monthAllowBlank,
29048 placeholder : this.monthPlaceholder,
29051 render : function (_self)
29053 this.el.select('span.input-group-addon', true).first().on('click', function(e){
29054 e.preventDefault();
29058 select : function (_self, oldvalue, newvalue)
29060 _this.setValue(_this.getValue());
29065 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
29067 this.yearField = new Roo.bootstrap.ComboBox({
29068 allowBlank : this.yearAllowBlank,
29069 alwaysQuery : true,
29070 displayField : 'value',
29073 forceSelection : true,
29075 placeholder : this.yearPlaceholder,
29076 selectOnFocus : true,
29077 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29078 triggerAction : 'all',
29080 valueField : 'value',
29081 store : new Roo.data.SimpleStore({
29082 data : (function() {
29084 _this.fireEvent('years', _this, years);
29087 fields : [ 'value' ]
29090 select : function (_self, record, index)
29092 _this.setValue(_this.getValue());
29097 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
29100 setValue : function(v, format)
29102 this.inputEl.dom.value = v;
29104 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
29106 var d = Date.parseDate(v, f);
29113 this.setDay(d.format(this.dayFormat));
29114 this.setMonth(d.format(this.monthFormat));
29115 this.setYear(d.format(this.yearFormat));
29122 setDay : function(v)
29124 this.dayField.setValue(v);
29125 this.inputEl.dom.value = this.getValue();
29130 setMonth : function(v)
29132 this.monthField.setValue(v, true);
29133 this.inputEl.dom.value = this.getValue();
29138 setYear : function(v)
29140 this.yearField.setValue(v);
29141 this.inputEl.dom.value = this.getValue();
29146 getDay : function()
29148 return this.dayField.getValue();
29151 getMonth : function()
29153 return this.monthField.getValue();
29156 getYear : function()
29158 return this.yearField.getValue();
29161 getValue : function()
29163 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
29165 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
29175 this.inputEl.dom.value = '';
29180 validate : function()
29182 var d = this.dayField.validate();
29183 var m = this.monthField.validate();
29184 var y = this.yearField.validate();
29189 (!this.dayAllowBlank && !d) ||
29190 (!this.monthAllowBlank && !m) ||
29191 (!this.yearAllowBlank && !y)
29196 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
29205 this.markInvalid();
29210 markValid : function()
29213 var label = this.el.select('label', true).first();
29214 var icon = this.el.select('i.fa-star', true).first();
29220 this.fireEvent('valid', this);
29224 * Mark this field as invalid
29225 * @param {String} msg The validation message
29227 markInvalid : function(msg)
29230 var label = this.el.select('label', true).first();
29231 var icon = this.el.select('i.fa-star', true).first();
29233 if(label && !icon){
29234 this.el.select('.roo-date-split-field-label', true).createChild({
29236 cls : 'text-danger fa fa-lg fa-star',
29237 tooltip : 'This field is required',
29238 style : 'margin-right:5px;'
29242 this.fireEvent('invalid', this, msg);
29245 clearInvalid : function()
29247 var label = this.el.select('label', true).first();
29248 var icon = this.el.select('i.fa-star', true).first();
29254 this.fireEvent('valid', this);
29257 getName: function()
29267 * http://masonry.desandro.com
29269 * The idea is to render all the bricks based on vertical width...
29271 * The original code extends 'outlayer' - we might need to use that....
29277 * @class Roo.bootstrap.LayoutMasonry
29278 * @extends Roo.bootstrap.Component
29279 * Bootstrap Layout Masonry class
29282 * Create a new Element
29283 * @param {Object} config The config object
29286 Roo.bootstrap.LayoutMasonry = function(config){
29287 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
29293 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
29296 * @cfg {Boolean} isLayoutInstant = no animation?
29298 isLayoutInstant : false, // needed?
29301 * @cfg {Number} boxWidth width of the columns
29306 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
29311 * @cfg {Number} padWidth padding below box..
29316 * @cfg {Number} gutter gutter width..
29321 * @cfg {Number} maxCols maximum number of columns
29327 * @cfg {Boolean} isAutoInitial defalut true
29329 isAutoInitial : true,
29334 * @cfg {Boolean} isHorizontal defalut false
29336 isHorizontal : false,
29338 currentSize : null,
29344 bricks: null, //CompositeElement
29348 _isLayoutInited : false,
29350 // isAlternative : false, // only use for vertical layout...
29353 * @cfg {Number} alternativePadWidth padding below box..
29355 alternativePadWidth : 50,
29357 getAutoCreate : function(){
29361 cls: 'blog-masonary-wrapper ' + this.cls,
29363 cls : 'mas-boxes masonary'
29370 getChildContainer: function( )
29372 if (this.boxesEl) {
29373 return this.boxesEl;
29376 this.boxesEl = this.el.select('.mas-boxes').first();
29378 return this.boxesEl;
29382 initEvents : function()
29386 if(this.isAutoInitial){
29387 Roo.log('hook children rendered');
29388 this.on('childrenrendered', function() {
29389 Roo.log('children rendered');
29395 initial : function()
29397 this.currentSize = this.el.getBox(true);
29399 Roo.EventManager.onWindowResize(this.resize, this);
29401 if(!this.isAutoInitial){
29409 //this.layout.defer(500,this);
29413 resize : function()
29417 var cs = this.el.getBox(true);
29419 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
29420 Roo.log("no change in with or X");
29424 this.currentSize = cs;
29430 layout : function()
29432 this._resetLayout();
29434 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
29436 this.layoutItems( isInstant );
29438 this._isLayoutInited = true;
29442 _resetLayout : function()
29444 if(this.isHorizontal){
29445 this.horizontalMeasureColumns();
29449 this.verticalMeasureColumns();
29453 verticalMeasureColumns : function()
29455 this.getContainerWidth();
29457 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29458 // this.colWidth = Math.floor(this.containerWidth * 0.8);
29462 var boxWidth = this.boxWidth + this.padWidth;
29464 if(this.containerWidth < this.boxWidth){
29465 boxWidth = this.containerWidth
29468 var containerWidth = this.containerWidth;
29470 var cols = Math.floor(containerWidth / boxWidth);
29472 this.cols = Math.max( cols, 1 );
29474 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
29476 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
29478 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
29480 this.colWidth = boxWidth + avail - this.padWidth;
29482 this.unitWidth = Math.floor((this.colWidth - (this.gutter * 2)) / 3);
29483 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
29486 horizontalMeasureColumns : function()
29488 this.getContainerWidth();
29490 var boxWidth = this.boxWidth;
29492 if(this.containerWidth < boxWidth){
29493 boxWidth = this.containerWidth;
29496 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
29498 this.el.setHeight(boxWidth);
29502 getContainerWidth : function()
29504 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
29507 layoutItems : function( isInstant )
29509 var items = Roo.apply([], this.bricks);
29511 if(this.isHorizontal){
29512 this._horizontalLayoutItems( items , isInstant );
29516 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29517 // this._verticalAlternativeLayoutItems( items , isInstant );
29521 this._verticalLayoutItems( items , isInstant );
29525 _verticalLayoutItems : function ( items , isInstant)
29527 if ( !items || !items.length ) {
29532 ['xs', 'xs', 'xs', 'tall'],
29533 ['xs', 'xs', 'tall'],
29534 ['xs', 'xs', 'sm'],
29535 ['xs', 'xs', 'xs'],
29541 ['sm', 'xs', 'xs'],
29545 ['tall', 'xs', 'xs', 'xs'],
29546 ['tall', 'xs', 'xs'],
29558 Roo.each(items, function(item, k){
29560 switch (item.size) {
29561 // these layouts take up a full box,
29572 boxes.push([item]);
29595 var filterPattern = function(box, length)
29603 var pattern = box.slice(0, length);
29607 Roo.each(pattern, function(i){
29608 format.push(i.size);
29611 Roo.each(standard, function(s){
29613 if(String(s) != String(format)){
29622 if(!match && length == 1){
29627 filterPattern(box, length - 1);
29631 queue.push(pattern);
29633 box = box.slice(length, box.length);
29635 filterPattern(box, 4);
29641 Roo.each(boxes, function(box, k){
29647 if(box.length == 1){
29652 filterPattern(box, 4);
29656 this._processVerticalLayoutQueue( queue, isInstant );
29660 // _verticalAlternativeLayoutItems : function( items , isInstant )
29662 // if ( !items || !items.length ) {
29666 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
29670 _horizontalLayoutItems : function ( items , isInstant)
29672 if ( !items || !items.length || items.length < 3) {
29678 var eItems = items.slice(0, 3);
29680 items = items.slice(3, items.length);
29683 ['xs', 'xs', 'xs', 'wide'],
29684 ['xs', 'xs', 'wide'],
29685 ['xs', 'xs', 'sm'],
29686 ['xs', 'xs', 'xs'],
29692 ['sm', 'xs', 'xs'],
29696 ['wide', 'xs', 'xs', 'xs'],
29697 ['wide', 'xs', 'xs'],
29710 Roo.each(items, function(item, k){
29712 switch (item.size) {
29723 boxes.push([item]);
29747 var filterPattern = function(box, length)
29755 var pattern = box.slice(0, length);
29759 Roo.each(pattern, function(i){
29760 format.push(i.size);
29763 Roo.each(standard, function(s){
29765 if(String(s) != String(format)){
29774 if(!match && length == 1){
29779 filterPattern(box, length - 1);
29783 queue.push(pattern);
29785 box = box.slice(length, box.length);
29787 filterPattern(box, 4);
29793 Roo.each(boxes, function(box, k){
29799 if(box.length == 1){
29804 filterPattern(box, 4);
29811 var pos = this.el.getBox(true);
29815 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29817 var hit_end = false;
29819 Roo.each(queue, function(box){
29823 Roo.each(box, function(b){
29825 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29835 Roo.each(box, function(b){
29837 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29840 mx = Math.max(mx, b.x);
29844 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
29848 Roo.each(box, function(b){
29850 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29864 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
29867 /** Sets position of item in DOM
29868 * @param {Element} item
29869 * @param {Number} x - horizontal position
29870 * @param {Number} y - vertical position
29871 * @param {Boolean} isInstant - disables transitions
29873 _processVerticalLayoutQueue : function( queue, isInstant )
29875 var pos = this.el.getBox(true);
29880 for (var i = 0; i < this.cols; i++){
29884 Roo.each(queue, function(box, k){
29886 var col = k % this.cols;
29888 Roo.each(box, function(b,kk){
29890 b.el.position('absolute');
29892 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29893 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29895 if(b.size == 'md-left' || b.size == 'md-right'){
29896 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29897 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29900 b.el.setWidth(width);
29901 b.el.setHeight(height);
29903 b.el.select('iframe',true).setSize(width,height);
29907 for (var i = 0; i < this.cols; i++){
29909 if(maxY[i] < maxY[col]){
29914 col = Math.min(col, i);
29918 x = pos.x + col * (this.colWidth + this.padWidth);
29922 var positions = [];
29924 switch (box.length){
29926 positions = this.getVerticalOneBoxColPositions(x, y, box);
29929 positions = this.getVerticalTwoBoxColPositions(x, y, box);
29932 positions = this.getVerticalThreeBoxColPositions(x, y, box);
29935 positions = this.getVerticalFourBoxColPositions(x, y, box);
29941 Roo.each(box, function(b,kk){
29943 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29945 var sz = b.el.getSize();
29947 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
29955 for (var i = 0; i < this.cols; i++){
29956 mY = Math.max(mY, maxY[i]);
29959 this.el.setHeight(mY - pos.y);
29963 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
29965 // var pos = this.el.getBox(true);
29968 // var maxX = pos.right;
29970 // var maxHeight = 0;
29972 // Roo.each(items, function(item, k){
29976 // item.el.position('absolute');
29978 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
29980 // item.el.setWidth(width);
29982 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
29984 // item.el.setHeight(height);
29987 // item.el.setXY([x, y], isInstant ? false : true);
29989 // item.el.setXY([maxX - width, y], isInstant ? false : true);
29992 // y = y + height + this.alternativePadWidth;
29994 // maxHeight = maxHeight + height + this.alternativePadWidth;
29998 // this.el.setHeight(maxHeight);
30002 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
30004 var pos = this.el.getBox(true);
30009 var maxX = pos.right;
30011 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
30013 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30015 Roo.each(queue, function(box, k){
30017 Roo.each(box, function(b, kk){
30019 b.el.position('absolute');
30021 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30022 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30024 if(b.size == 'md-left' || b.size == 'md-right'){
30025 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30026 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30029 b.el.setWidth(width);
30030 b.el.setHeight(height);
30038 var positions = [];
30040 switch (box.length){
30042 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
30045 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
30048 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
30051 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
30057 Roo.each(box, function(b,kk){
30059 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30061 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
30069 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
30071 Roo.each(eItems, function(b,k){
30073 b.size = (k == 0) ? 'sm' : 'xs';
30074 b.x = (k == 0) ? 2 : 1;
30075 b.y = (k == 0) ? 2 : 1;
30077 b.el.position('absolute');
30079 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30081 b.el.setWidth(width);
30083 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30085 b.el.setHeight(height);
30089 var positions = [];
30092 x : maxX - this.unitWidth * 2 - this.gutter,
30097 x : maxX - this.unitWidth,
30098 y : minY + (this.unitWidth + this.gutter) * 2
30102 x : maxX - this.unitWidth * 3 - this.gutter * 2,
30106 Roo.each(eItems, function(b,k){
30108 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
30114 getVerticalOneBoxColPositions : function(x, y, box)
30118 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
30120 if(box[0].size == 'md-left'){
30124 if(box[0].size == 'md-right'){
30129 x : x + (this.unitWidth + this.gutter) * rand,
30136 getVerticalTwoBoxColPositions : function(x, y, box)
30140 if(box[0].size == 'xs'){
30144 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
30148 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
30162 x : x + (this.unitWidth + this.gutter) * 2,
30163 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
30170 getVerticalThreeBoxColPositions : function(x, y, box)
30174 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
30182 x : x + (this.unitWidth + this.gutter) * 1,
30187 x : x + (this.unitWidth + this.gutter) * 2,
30195 if(box[0].size == 'xs' && box[1].size == 'xs'){
30204 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
30208 x : x + (this.unitWidth + this.gutter) * 1,
30222 x : x + (this.unitWidth + this.gutter) * 2,
30227 x : x + (this.unitWidth + this.gutter) * 2,
30228 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
30235 getVerticalFourBoxColPositions : function(x, y, box)
30239 if(box[0].size == 'xs'){
30248 y : y + (this.unitHeight + this.gutter) * 1
30253 y : y + (this.unitHeight + this.gutter) * 2
30257 x : x + (this.unitWidth + this.gutter) * 1,
30271 x : x + (this.unitWidth + this.gutter) * 2,
30276 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
30277 y : y + (this.unitHeight + this.gutter) * 1
30281 x : x + (this.unitWidth + this.gutter) * 2,
30282 y : y + (this.unitWidth + this.gutter) * 2
30289 getHorizontalOneBoxColPositions : function(maxX, minY, box)
30293 if(box[0].size == 'md-left'){
30295 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
30302 if(box[0].size == 'md-right'){
30304 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
30305 y : minY + (this.unitWidth + this.gutter) * 1
30311 var rand = Math.floor(Math.random() * (4 - box[0].y));
30314 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30315 y : minY + (this.unitWidth + this.gutter) * rand
30322 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
30326 if(box[0].size == 'xs'){
30329 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30334 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30335 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
30343 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30348 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30349 y : minY + (this.unitWidth + this.gutter) * 2
30356 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
30360 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
30363 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30368 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30369 y : minY + (this.unitWidth + this.gutter) * 1
30373 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30374 y : minY + (this.unitWidth + this.gutter) * 2
30381 if(box[0].size == 'xs' && box[1].size == 'xs'){
30384 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30389 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30394 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30395 y : minY + (this.unitWidth + this.gutter) * 1
30403 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30408 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30409 y : minY + (this.unitWidth + this.gutter) * 2
30413 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30414 y : minY + (this.unitWidth + this.gutter) * 2
30421 getHorizontalFourBoxColPositions : function(maxX, minY, box)
30425 if(box[0].size == 'xs'){
30428 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30433 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30438 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),
30443 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
30444 y : minY + (this.unitWidth + this.gutter) * 1
30452 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30457 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30458 y : minY + (this.unitWidth + this.gutter) * 2
30462 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30463 y : minY + (this.unitWidth + this.gutter) * 2
30467 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),
30468 y : minY + (this.unitWidth + this.gutter) * 2
30482 * http://masonry.desandro.com
30484 * The idea is to render all the bricks based on vertical width...
30486 * The original code extends 'outlayer' - we might need to use that....
30492 * @class Roo.bootstrap.LayoutMasonryAuto
30493 * @extends Roo.bootstrap.Component
30494 * Bootstrap Layout Masonry class
30497 * Create a new Element
30498 * @param {Object} config The config object
30501 Roo.bootstrap.LayoutMasonryAuto = function(config){
30502 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
30505 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
30508 * @cfg {Boolean} isFitWidth - resize the width..
30510 isFitWidth : false, // options..
30512 * @cfg {Boolean} isOriginLeft = left align?
30514 isOriginLeft : true,
30516 * @cfg {Boolean} isOriginTop = top align?
30518 isOriginTop : false,
30520 * @cfg {Boolean} isLayoutInstant = no animation?
30522 isLayoutInstant : false, // needed?
30524 * @cfg {Boolean} isResizingContainer = not sure if this is used..
30526 isResizingContainer : true,
30528 * @cfg {Number} columnWidth width of the columns
30534 * @cfg {Number} maxCols maximum number of columns
30539 * @cfg {Number} padHeight padding below box..
30545 * @cfg {Boolean} isAutoInitial defalut true
30548 isAutoInitial : true,
30554 initialColumnWidth : 0,
30555 currentSize : null,
30557 colYs : null, // array.
30564 bricks: null, //CompositeElement
30565 cols : 0, // array?
30566 // element : null, // wrapped now this.el
30567 _isLayoutInited : null,
30570 getAutoCreate : function(){
30574 cls: 'blog-masonary-wrapper ' + this.cls,
30576 cls : 'mas-boxes masonary'
30583 getChildContainer: function( )
30585 if (this.boxesEl) {
30586 return this.boxesEl;
30589 this.boxesEl = this.el.select('.mas-boxes').first();
30591 return this.boxesEl;
30595 initEvents : function()
30599 if(this.isAutoInitial){
30600 Roo.log('hook children rendered');
30601 this.on('childrenrendered', function() {
30602 Roo.log('children rendered');
30609 initial : function()
30611 this.reloadItems();
30613 this.currentSize = this.el.getBox(true);
30615 /// was window resize... - let's see if this works..
30616 Roo.EventManager.onWindowResize(this.resize, this);
30618 if(!this.isAutoInitial){
30623 this.layout.defer(500,this);
30626 reloadItems: function()
30628 this.bricks = this.el.select('.masonry-brick', true);
30630 this.bricks.each(function(b) {
30631 //Roo.log(b.getSize());
30632 if (!b.attr('originalwidth')) {
30633 b.attr('originalwidth', b.getSize().width);
30638 Roo.log(this.bricks.elements.length);
30641 resize : function()
30644 var cs = this.el.getBox(true);
30646 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
30647 Roo.log("no change in with or X");
30650 this.currentSize = cs;
30654 layout : function()
30657 this._resetLayout();
30658 //this._manageStamps();
30660 // don't animate first layout
30661 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30662 this.layoutItems( isInstant );
30664 // flag for initalized
30665 this._isLayoutInited = true;
30668 layoutItems : function( isInstant )
30670 //var items = this._getItemsForLayout( this.items );
30671 // original code supports filtering layout items.. we just ignore it..
30673 this._layoutItems( this.bricks , isInstant );
30675 this._postLayout();
30677 _layoutItems : function ( items , isInstant)
30679 //this.fireEvent( 'layout', this, items );
30682 if ( !items || !items.elements.length ) {
30683 // no items, emit event with empty array
30688 items.each(function(item) {
30689 Roo.log("layout item");
30691 // get x/y object from method
30692 var position = this._getItemLayoutPosition( item );
30694 position.item = item;
30695 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
30696 queue.push( position );
30699 this._processLayoutQueue( queue );
30701 /** Sets position of item in DOM
30702 * @param {Element} item
30703 * @param {Number} x - horizontal position
30704 * @param {Number} y - vertical position
30705 * @param {Boolean} isInstant - disables transitions
30707 _processLayoutQueue : function( queue )
30709 for ( var i=0, len = queue.length; i < len; i++ ) {
30710 var obj = queue[i];
30711 obj.item.position('absolute');
30712 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
30718 * Any logic you want to do after each layout,
30719 * i.e. size the container
30721 _postLayout : function()
30723 this.resizeContainer();
30726 resizeContainer : function()
30728 if ( !this.isResizingContainer ) {
30731 var size = this._getContainerSize();
30733 this.el.setSize(size.width,size.height);
30734 this.boxesEl.setSize(size.width,size.height);
30740 _resetLayout : function()
30742 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
30743 this.colWidth = this.el.getWidth();
30744 //this.gutter = this.el.getWidth();
30746 this.measureColumns();
30752 this.colYs.push( 0 );
30758 measureColumns : function()
30760 this.getContainerWidth();
30761 // if columnWidth is 0, default to outerWidth of first item
30762 if ( !this.columnWidth ) {
30763 var firstItem = this.bricks.first();
30764 Roo.log(firstItem);
30765 this.columnWidth = this.containerWidth;
30766 if (firstItem && firstItem.attr('originalwidth') ) {
30767 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
30769 // columnWidth fall back to item of first element
30770 Roo.log("set column width?");
30771 this.initialColumnWidth = this.columnWidth ;
30773 // if first elem has no width, default to size of container
30778 if (this.initialColumnWidth) {
30779 this.columnWidth = this.initialColumnWidth;
30784 // column width is fixed at the top - however if container width get's smaller we should
30787 // this bit calcs how man columns..
30789 var columnWidth = this.columnWidth += this.gutter;
30791 // calculate columns
30792 var containerWidth = this.containerWidth + this.gutter;
30794 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
30795 // fix rounding errors, typically with gutters
30796 var excess = columnWidth - containerWidth % columnWidth;
30799 // if overshoot is less than a pixel, round up, otherwise floor it
30800 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
30801 cols = Math[ mathMethod ]( cols );
30802 this.cols = Math.max( cols, 1 );
30803 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30805 // padding positioning..
30806 var totalColWidth = this.cols * this.columnWidth;
30807 var padavail = this.containerWidth - totalColWidth;
30808 // so for 2 columns - we need 3 'pads'
30810 var padNeeded = (1+this.cols) * this.padWidth;
30812 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
30814 this.columnWidth += padExtra
30815 //this.padWidth = Math.floor(padavail / ( this.cols));
30817 // adjust colum width so that padding is fixed??
30819 // we have 3 columns ... total = width * 3
30820 // we have X left over... that should be used by
30822 //if (this.expandC) {
30830 getContainerWidth : function()
30832 /* // container is parent if fit width
30833 var container = this.isFitWidth ? this.element.parentNode : this.element;
30834 // check that this.size and size are there
30835 // IE8 triggers resize on body size change, so they might not be
30837 var size = getSize( container ); //FIXME
30838 this.containerWidth = size && size.innerWidth; //FIXME
30841 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
30845 _getItemLayoutPosition : function( item ) // what is item?
30847 // we resize the item to our columnWidth..
30849 item.setWidth(this.columnWidth);
30850 item.autoBoxAdjust = false;
30852 var sz = item.getSize();
30854 // how many columns does this brick span
30855 var remainder = this.containerWidth % this.columnWidth;
30857 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
30858 // round if off by 1 pixel, otherwise use ceil
30859 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
30860 colSpan = Math.min( colSpan, this.cols );
30862 // normally this should be '1' as we dont' currently allow multi width columns..
30864 var colGroup = this._getColGroup( colSpan );
30865 // get the minimum Y value from the columns
30866 var minimumY = Math.min.apply( Math, colGroup );
30867 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
30869 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
30871 // position the brick
30873 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
30874 y: this.currentSize.y + minimumY + this.padHeight
30878 // apply setHeight to necessary columns
30879 var setHeight = minimumY + sz.height + this.padHeight;
30880 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
30882 var setSpan = this.cols + 1 - colGroup.length;
30883 for ( var i = 0; i < setSpan; i++ ) {
30884 this.colYs[ shortColIndex + i ] = setHeight ;
30891 * @param {Number} colSpan - number of columns the element spans
30892 * @returns {Array} colGroup
30894 _getColGroup : function( colSpan )
30896 if ( colSpan < 2 ) {
30897 // if brick spans only one column, use all the column Ys
30902 // how many different places could this brick fit horizontally
30903 var groupCount = this.cols + 1 - colSpan;
30904 // for each group potential horizontal position
30905 for ( var i = 0; i < groupCount; i++ ) {
30906 // make an array of colY values for that one group
30907 var groupColYs = this.colYs.slice( i, i + colSpan );
30908 // and get the max value of the array
30909 colGroup[i] = Math.max.apply( Math, groupColYs );
30914 _manageStamp : function( stamp )
30916 var stampSize = stamp.getSize();
30917 var offset = stamp.getBox();
30918 // get the columns that this stamp affects
30919 var firstX = this.isOriginLeft ? offset.x : offset.right;
30920 var lastX = firstX + stampSize.width;
30921 var firstCol = Math.floor( firstX / this.columnWidth );
30922 firstCol = Math.max( 0, firstCol );
30924 var lastCol = Math.floor( lastX / this.columnWidth );
30925 // lastCol should not go over if multiple of columnWidth #425
30926 lastCol -= lastX % this.columnWidth ? 0 : 1;
30927 lastCol = Math.min( this.cols - 1, lastCol );
30929 // set colYs to bottom of the stamp
30930 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
30933 for ( var i = firstCol; i <= lastCol; i++ ) {
30934 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
30939 _getContainerSize : function()
30941 this.maxY = Math.max.apply( Math, this.colYs );
30946 if ( this.isFitWidth ) {
30947 size.width = this._getContainerFitWidth();
30953 _getContainerFitWidth : function()
30955 var unusedCols = 0;
30956 // count unused columns
30959 if ( this.colYs[i] !== 0 ) {
30964 // fit container to columns that have been used
30965 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
30968 needsResizeLayout : function()
30970 var previousWidth = this.containerWidth;
30971 this.getContainerWidth();
30972 return previousWidth !== this.containerWidth;
30987 * @class Roo.bootstrap.MasonryBrick
30988 * @extends Roo.bootstrap.Component
30989 * Bootstrap MasonryBrick class
30992 * Create a new MasonryBrick
30993 * @param {Object} config The config object
30996 Roo.bootstrap.MasonryBrick = function(config){
30997 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
31003 * When a MasonryBrick is clcik
31004 * @param {Roo.bootstrap.MasonryBrick} this
31005 * @param {Roo.EventObject} e
31011 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
31014 * @cfg {String} title
31018 * @cfg {String} html
31022 * @cfg {String} bgimage
31026 * @cfg {String} videourl
31030 * @cfg {String} cls
31034 * @cfg {String} href
31038 * @cfg {String} (xs|sm|md|md-left|md-right|tall|wide) size
31043 * @cfg {String} (center|bottom) placetitle
31048 * @cfg {Boolean} isFitContainer defalut true
31050 isFitContainer : true,
31053 * @cfg {Boolean} preventDefault defalut false
31055 preventDefault : false,
31057 getAutoCreate : function()
31059 if(!this.isFitContainer){
31060 return this.getSplitAutoCreate();
31063 var cls = 'masonry-brick masonry-brick-full';
31065 if(this.href.length){
31066 cls += ' masonry-brick-link';
31069 if(this.bgimage.length){
31070 cls += ' masonry-brick-image';
31073 if(!this.html.length){
31074 cls += ' enable-mask';
31078 cls += ' masonry-' + this.size + '-brick';
31081 if(this.placetitle.length){
31083 switch (this.placetitle) {
31085 cls += ' masonry-center-title';
31088 cls += ' masonry-bottom-title';
31095 if(!this.html.length && !this.bgimage.length){
31096 cls += ' masonry-center-title';
31099 if(!this.html.length && this.bgimage.length){
31100 cls += ' masonry-bottom-title';
31105 cls += ' ' + this.cls;
31109 tag: (this.href.length) ? 'a' : 'div',
31114 cls: 'masonry-brick-paragraph',
31120 if(this.href.length){
31121 cfg.href = this.href;
31124 var cn = cfg.cn[0].cn;
31126 if(this.title.length){
31129 cls: 'masonry-brick-title',
31134 if(this.html.length){
31137 cls: 'masonry-brick-text',
31141 if (!this.title.length && !this.html.length) {
31142 cfg.cn[0].cls += ' hide';
31145 if(this.bgimage.length){
31148 cls: 'masonry-brick-image-view',
31153 if(this.videourl.length){
31154 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
31155 // youtube support only?
31158 cls: 'masonry-brick-image-view',
31161 allowfullscreen : true
31169 cls: 'masonry-brick-mask'
31176 getSplitAutoCreate : function()
31178 var cls = 'masonry-brick masonry-brick-split';
31180 if(this.href.length){
31181 cls += ' masonry-brick-link';
31184 if(this.bgimage.length){
31185 cls += ' masonry-brick-image';
31189 cls += ' masonry-' + this.size + '-brick';
31192 switch (this.placetitle) {
31194 cls += ' masonry-center-title';
31197 cls += ' masonry-bottom-title';
31200 if(!this.bgimage.length){
31201 cls += ' masonry-center-title';
31204 if(this.bgimage.length){
31205 cls += ' masonry-bottom-title';
31211 cls += ' ' + this.cls;
31215 tag: (this.href.length) ? 'a' : 'div',
31220 cls: 'masonry-brick-split-head',
31224 cls: 'masonry-brick-paragraph',
31231 cls: 'masonry-brick-split-body',
31237 if(this.href.length){
31238 cfg.href = this.href;
31241 if(this.title.length){
31242 cfg.cn[0].cn[0].cn.push({
31244 cls: 'masonry-brick-title',
31249 if(this.html.length){
31250 cfg.cn[1].cn.push({
31252 cls: 'masonry-brick-text',
31257 if(this.bgimage.length){
31258 cfg.cn[0].cn.push({
31260 cls: 'masonry-brick-image-view',
31265 if(this.videourl.length){
31266 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
31267 // youtube support only?
31268 cfg.cn[0].cn.cn.push({
31270 cls: 'masonry-brick-image-view',
31273 allowfullscreen : true
31280 initEvents: function()
31282 switch (this.size) {
31315 this.el.on('touchstart', this.onTouchStart, this);
31316 this.el.on('touchmove', this.onTouchMove, this);
31317 this.el.on('touchend', this.onTouchEnd, this);
31318 this.el.on('contextmenu', this.onContextMenu, this);
31320 this.el.on('mouseenter' ,this.enter, this);
31321 this.el.on('mouseleave', this.leave, this);
31322 this.el.on('click', this.onClick, this);
31325 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
31326 this.parent().bricks.push(this);
31331 onClick: function(e, el)
31333 var time = this.endTimer - this.startTimer;
31337 e.preventDefault();
31342 if(!this.preventDefault){
31346 e.preventDefault();
31347 this.fireEvent('click', this);
31350 enter: function(e, el)
31352 e.preventDefault();
31354 if(!this.isFitContainer){
31358 if(this.bgimage.length && this.html.length){
31359 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
31363 leave: function(e, el)
31365 e.preventDefault();
31367 if(!this.isFitContainer){
31371 if(this.bgimage.length && this.html.length){
31372 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
31376 onTouchStart: function(e, el)
31378 // e.preventDefault();
31380 this.touchmoved = false;
31382 if(!this.isFitContainer){
31386 if(!this.bgimage.length || !this.html.length){
31390 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
31392 this.timer = new Date().getTime();
31396 onTouchMove: function(e, el)
31398 this.touchmoved = true;
31401 onContextMenu : function(e,el)
31403 e.preventDefault();
31404 e.stopPropagation();
31408 onTouchEnd: function(e, el)
31410 // e.preventDefault();
31412 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
31419 if(!this.bgimage.length || !this.html.length){
31421 if(this.href.length){
31422 window.location.href = this.href;
31428 if(!this.isFitContainer){
31432 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
31434 window.location.href = this.href;
31449 * @class Roo.bootstrap.Brick
31450 * @extends Roo.bootstrap.Component
31451 * Bootstrap Brick class
31454 * Create a new Brick
31455 * @param {Object} config The config object
31458 Roo.bootstrap.Brick = function(config){
31459 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
31465 * When a Brick is click
31466 * @param {Roo.bootstrap.Brick} this
31467 * @param {Roo.EventObject} e
31473 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
31476 * @cfg {String} title
31480 * @cfg {String} html
31484 * @cfg {String} bgimage
31488 * @cfg {String} cls
31492 * @cfg {String} href
31496 * @cfg {String} video
31500 * @cfg {Boolean} square
31504 getAutoCreate : function()
31506 var cls = 'roo-brick';
31508 if(this.href.length){
31509 cls += ' roo-brick-link';
31512 if(this.bgimage.length){
31513 cls += ' roo-brick-image';
31516 if(!this.html.length && !this.bgimage.length){
31517 cls += ' roo-brick-center-title';
31520 if(!this.html.length && this.bgimage.length){
31521 cls += ' roo-brick-bottom-title';
31525 cls += ' ' + this.cls;
31529 tag: (this.href.length) ? 'a' : 'div',
31534 cls: 'roo-brick-paragraph',
31540 if(this.href.length){
31541 cfg.href = this.href;
31544 var cn = cfg.cn[0].cn;
31546 if(this.title.length){
31549 cls: 'roo-brick-title',
31554 if(this.html.length){
31557 cls: 'roo-brick-text',
31564 if(this.bgimage.length){
31567 cls: 'roo-brick-image-view',
31575 initEvents: function()
31577 if(this.title.length || this.html.length){
31578 this.el.on('mouseenter' ,this.enter, this);
31579 this.el.on('mouseleave', this.leave, this);
31583 Roo.EventManager.onWindowResize(this.resize, this);
31588 resize : function()
31590 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
31592 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
31594 if(this.bgimage.length){
31595 var image = this.el.select('.roo-brick-image-view', true).first();
31596 image.setWidth(paragraph.getWidth());
31597 image.setHeight(paragraph.getWidth());
31599 this.el.setHeight(paragraph.getWidth());
31605 enter: function(e, el)
31607 e.preventDefault();
31609 if(this.bgimage.length){
31610 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
31611 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
31615 leave: function(e, el)
31617 e.preventDefault();
31619 if(this.bgimage.length){
31620 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
31621 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
31631 * Ext JS Library 1.1.1
31632 * Copyright(c) 2006-2007, Ext JS, LLC.
31634 * Originally Released Under LGPL - original licence link has changed is not relivant.
31637 * <script type="text/javascript">
31642 * @class Roo.bootstrap.SplitBar
31643 * @extends Roo.util.Observable
31644 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
31648 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
31649 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
31650 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
31651 split.minSize = 100;
31652 split.maxSize = 600;
31653 split.animate = true;
31654 split.on('moved', splitterMoved);
31657 * Create a new SplitBar
31658 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
31659 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
31660 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
31661 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
31662 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
31663 position of the SplitBar).
31665 Roo.bootstrap.SplitBar = function(cfg){
31670 // dragElement : elm
31671 // resizingElement: el,
31673 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
31674 // placement : Roo.bootstrap.SplitBar.LEFT ,
31675 // existingProxy ???
31678 this.el = Roo.get(cfg.dragElement, true);
31679 this.el.dom.unselectable = "on";
31681 this.resizingEl = Roo.get(cfg.resizingElement, true);
31685 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
31686 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
31689 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
31692 * The minimum size of the resizing element. (Defaults to 0)
31698 * The maximum size of the resizing element. (Defaults to 2000)
31701 this.maxSize = 2000;
31704 * Whether to animate the transition to the new size
31707 this.animate = false;
31710 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
31713 this.useShim = false;
31718 if(!cfg.existingProxy){
31720 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
31722 this.proxy = Roo.get(cfg.existingProxy).dom;
31725 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
31728 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
31731 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
31734 this.dragSpecs = {};
31737 * @private The adapter to use to positon and resize elements
31739 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
31740 this.adapter.init(this);
31742 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31744 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
31745 this.el.addClass("roo-splitbar-h");
31748 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
31749 this.el.addClass("roo-splitbar-v");
31755 * Fires when the splitter is moved (alias for {@link #event-moved})
31756 * @param {Roo.bootstrap.SplitBar} this
31757 * @param {Number} newSize the new width or height
31762 * Fires when the splitter is moved
31763 * @param {Roo.bootstrap.SplitBar} this
31764 * @param {Number} newSize the new width or height
31768 * @event beforeresize
31769 * Fires before the splitter is dragged
31770 * @param {Roo.bootstrap.SplitBar} this
31772 "beforeresize" : true,
31774 "beforeapply" : true
31777 Roo.util.Observable.call(this);
31780 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
31781 onStartProxyDrag : function(x, y){
31782 this.fireEvent("beforeresize", this);
31784 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
31786 o.enableDisplayMode("block");
31787 // all splitbars share the same overlay
31788 Roo.bootstrap.SplitBar.prototype.overlay = o;
31790 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
31791 this.overlay.show();
31792 Roo.get(this.proxy).setDisplayed("block");
31793 var size = this.adapter.getElementSize(this);
31794 this.activeMinSize = this.getMinimumSize();;
31795 this.activeMaxSize = this.getMaximumSize();;
31796 var c1 = size - this.activeMinSize;
31797 var c2 = Math.max(this.activeMaxSize - size, 0);
31798 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31799 this.dd.resetConstraints();
31800 this.dd.setXConstraint(
31801 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
31802 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
31804 this.dd.setYConstraint(0, 0);
31806 this.dd.resetConstraints();
31807 this.dd.setXConstraint(0, 0);
31808 this.dd.setYConstraint(
31809 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
31810 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
31813 this.dragSpecs.startSize = size;
31814 this.dragSpecs.startPoint = [x, y];
31815 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
31819 * @private Called after the drag operation by the DDProxy
31821 onEndProxyDrag : function(e){
31822 Roo.get(this.proxy).setDisplayed(false);
31823 var endPoint = Roo.lib.Event.getXY(e);
31825 this.overlay.hide();
31828 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31829 newSize = this.dragSpecs.startSize +
31830 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
31831 endPoint[0] - this.dragSpecs.startPoint[0] :
31832 this.dragSpecs.startPoint[0] - endPoint[0]
31835 newSize = this.dragSpecs.startSize +
31836 (this.placement == Roo.bootstrap.SplitBar.TOP ?
31837 endPoint[1] - this.dragSpecs.startPoint[1] :
31838 this.dragSpecs.startPoint[1] - endPoint[1]
31841 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
31842 if(newSize != this.dragSpecs.startSize){
31843 if(this.fireEvent('beforeapply', this, newSize) !== false){
31844 this.adapter.setElementSize(this, newSize);
31845 this.fireEvent("moved", this, newSize);
31846 this.fireEvent("resize", this, newSize);
31852 * Get the adapter this SplitBar uses
31853 * @return The adapter object
31855 getAdapter : function(){
31856 return this.adapter;
31860 * Set the adapter this SplitBar uses
31861 * @param {Object} adapter A SplitBar adapter object
31863 setAdapter : function(adapter){
31864 this.adapter = adapter;
31865 this.adapter.init(this);
31869 * Gets the minimum size for the resizing element
31870 * @return {Number} The minimum size
31872 getMinimumSize : function(){
31873 return this.minSize;
31877 * Sets the minimum size for the resizing element
31878 * @param {Number} minSize The minimum size
31880 setMinimumSize : function(minSize){
31881 this.minSize = minSize;
31885 * Gets the maximum size for the resizing element
31886 * @return {Number} The maximum size
31888 getMaximumSize : function(){
31889 return this.maxSize;
31893 * Sets the maximum size for the resizing element
31894 * @param {Number} maxSize The maximum size
31896 setMaximumSize : function(maxSize){
31897 this.maxSize = maxSize;
31901 * Sets the initialize size for the resizing element
31902 * @param {Number} size The initial size
31904 setCurrentSize : function(size){
31905 var oldAnimate = this.animate;
31906 this.animate = false;
31907 this.adapter.setElementSize(this, size);
31908 this.animate = oldAnimate;
31912 * Destroy this splitbar.
31913 * @param {Boolean} removeEl True to remove the element
31915 destroy : function(removeEl){
31917 this.shim.remove();
31920 this.proxy.parentNode.removeChild(this.proxy);
31928 * @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.
31930 Roo.bootstrap.SplitBar.createProxy = function(dir){
31931 var proxy = new Roo.Element(document.createElement("div"));
31932 proxy.unselectable();
31933 var cls = 'roo-splitbar-proxy';
31934 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
31935 document.body.appendChild(proxy.dom);
31940 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
31941 * Default Adapter. It assumes the splitter and resizing element are not positioned
31942 * elements and only gets/sets the width of the element. Generally used for table based layouts.
31944 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
31947 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
31948 // do nothing for now
31949 init : function(s){
31953 * Called before drag operations to get the current size of the resizing element.
31954 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
31956 getElementSize : function(s){
31957 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31958 return s.resizingEl.getWidth();
31960 return s.resizingEl.getHeight();
31965 * Called after drag operations to set the size of the resizing element.
31966 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
31967 * @param {Number} newSize The new size to set
31968 * @param {Function} onComplete A function to be invoked when resizing is complete
31970 setElementSize : function(s, newSize, onComplete){
31971 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31973 s.resizingEl.setWidth(newSize);
31975 onComplete(s, newSize);
31978 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
31983 s.resizingEl.setHeight(newSize);
31985 onComplete(s, newSize);
31988 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
31995 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
31996 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
31997 * Adapter that moves the splitter element to align with the resized sizing element.
31998 * Used with an absolute positioned SplitBar.
31999 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
32000 * document.body, make sure you assign an id to the body element.
32002 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
32003 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
32004 this.container = Roo.get(container);
32007 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
32008 init : function(s){
32009 this.basic.init(s);
32012 getElementSize : function(s){
32013 return this.basic.getElementSize(s);
32016 setElementSize : function(s, newSize, onComplete){
32017 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
32020 moveSplitter : function(s){
32021 var yes = Roo.bootstrap.SplitBar;
32022 switch(s.placement){
32024 s.el.setX(s.resizingEl.getRight());
32027 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
32030 s.el.setY(s.resizingEl.getBottom());
32033 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
32040 * Orientation constant - Create a vertical SplitBar
32044 Roo.bootstrap.SplitBar.VERTICAL = 1;
32047 * Orientation constant - Create a horizontal SplitBar
32051 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
32054 * Placement constant - The resizing element is to the left of the splitter element
32058 Roo.bootstrap.SplitBar.LEFT = 1;
32061 * Placement constant - The resizing element is to the right of the splitter element
32065 Roo.bootstrap.SplitBar.RIGHT = 2;
32068 * Placement constant - The resizing element is positioned above the splitter element
32072 Roo.bootstrap.SplitBar.TOP = 3;
32075 * Placement constant - The resizing element is positioned under splitter element
32079 Roo.bootstrap.SplitBar.BOTTOM = 4;
32080 Roo.namespace("Roo.bootstrap.layout");/*
32082 * Ext JS Library 1.1.1
32083 * Copyright(c) 2006-2007, Ext JS, LLC.
32085 * Originally Released Under LGPL - original licence link has changed is not relivant.
32088 * <script type="text/javascript">
32092 * @class Roo.bootstrap.layout.Manager
32093 * @extends Roo.bootstrap.Component
32094 * Base class for layout managers.
32096 Roo.bootstrap.layout.Manager = function(config)
32098 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
32104 /** false to disable window resize monitoring @type Boolean */
32105 this.monitorWindowResize = true;
32110 * Fires when a layout is performed.
32111 * @param {Roo.LayoutManager} this
32115 * @event regionresized
32116 * Fires when the user resizes a region.
32117 * @param {Roo.LayoutRegion} region The resized region
32118 * @param {Number} newSize The new size (width for east/west, height for north/south)
32120 "regionresized" : true,
32122 * @event regioncollapsed
32123 * Fires when a region is collapsed.
32124 * @param {Roo.LayoutRegion} region The collapsed region
32126 "regioncollapsed" : true,
32128 * @event regionexpanded
32129 * Fires when a region is expanded.
32130 * @param {Roo.LayoutRegion} region The expanded region
32132 "regionexpanded" : true
32134 this.updating = false;
32137 this.el = Roo.get(config.el);
32143 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
32148 monitorWindowResize : true,
32154 onRender : function(ct, position)
32157 this.el = Roo.get(ct);
32160 //this.fireEvent('render',this);
32164 initEvents: function()
32168 // ie scrollbar fix
32169 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
32170 document.body.scroll = "no";
32171 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
32172 this.el.position('relative');
32174 this.id = this.el.id;
32175 this.el.addClass("roo-layout-container");
32176 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
32177 if(this.el.dom != document.body ) {
32178 this.el.on('resize', this.layout,this);
32179 this.el.on('show', this.layout,this);
32185 * Returns true if this layout is currently being updated
32186 * @return {Boolean}
32188 isUpdating : function(){
32189 return this.updating;
32193 * Suspend the LayoutManager from doing auto-layouts while
32194 * making multiple add or remove calls
32196 beginUpdate : function(){
32197 this.updating = true;
32201 * Restore auto-layouts and optionally disable the manager from performing a layout
32202 * @param {Boolean} noLayout true to disable a layout update
32204 endUpdate : function(noLayout){
32205 this.updating = false;
32211 layout: function(){
32215 onRegionResized : function(region, newSize){
32216 this.fireEvent("regionresized", region, newSize);
32220 onRegionCollapsed : function(region){
32221 this.fireEvent("regioncollapsed", region);
32224 onRegionExpanded : function(region){
32225 this.fireEvent("regionexpanded", region);
32229 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
32230 * performs box-model adjustments.
32231 * @return {Object} The size as an object {width: (the width), height: (the height)}
32233 getViewSize : function()
32236 if(this.el.dom != document.body){
32237 size = this.el.getSize();
32239 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
32241 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
32242 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
32247 * Returns the Element this layout is bound to.
32248 * @return {Roo.Element}
32250 getEl : function(){
32255 * Returns the specified region.
32256 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
32257 * @return {Roo.LayoutRegion}
32259 getRegion : function(target){
32260 return this.regions[target.toLowerCase()];
32263 onWindowResize : function(){
32264 if(this.monitorWindowResize){
32271 * Ext JS Library 1.1.1
32272 * Copyright(c) 2006-2007, Ext JS, LLC.
32274 * Originally Released Under LGPL - original licence link has changed is not relivant.
32277 * <script type="text/javascript">
32280 * @class Roo.bootstrap.layout.Border
32281 * @extends Roo.bootstrap.layout.Manager
32282 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
32283 * please see: examples/bootstrap/nested.html<br><br>
32285 <b>The container the layout is rendered into can be either the body element or any other element.
32286 If it is not the body element, the container needs to either be an absolute positioned element,
32287 or you will need to add "position:relative" to the css of the container. You will also need to specify
32288 the container size if it is not the body element.</b>
32291 * Create a new Border
32292 * @param {Object} config Configuration options
32294 Roo.bootstrap.layout.Border = function(config){
32295 config = config || {};
32296 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
32300 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
32301 if(config[region]){
32302 config[region].region = region;
32303 this.addRegion(config[region]);
32309 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
32311 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
32313 * Creates and adds a new region if it doesn't already exist.
32314 * @param {String} target The target region key (north, south, east, west or center).
32315 * @param {Object} config The regions config object
32316 * @return {BorderLayoutRegion} The new region
32318 addRegion : function(config)
32320 if(!this.regions[config.region]){
32321 var r = this.factory(config);
32322 this.bindRegion(r);
32324 return this.regions[config.region];
32328 bindRegion : function(r){
32329 this.regions[r.config.region] = r;
32331 r.on("visibilitychange", this.layout, this);
32332 r.on("paneladded", this.layout, this);
32333 r.on("panelremoved", this.layout, this);
32334 r.on("invalidated", this.layout, this);
32335 r.on("resized", this.onRegionResized, this);
32336 r.on("collapsed", this.onRegionCollapsed, this);
32337 r.on("expanded", this.onRegionExpanded, this);
32341 * Performs a layout update.
32343 layout : function()
32345 if(this.updating) {
32349 // render all the rebions if they have not been done alreayd?
32350 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
32351 if(this.regions[region] && !this.regions[region].bodyEl){
32352 this.regions[region].onRender(this.el)
32356 var size = this.getViewSize();
32357 var w = size.width;
32358 var h = size.height;
32363 //var x = 0, y = 0;
32365 var rs = this.regions;
32366 var north = rs["north"];
32367 var south = rs["south"];
32368 var west = rs["west"];
32369 var east = rs["east"];
32370 var center = rs["center"];
32371 //if(this.hideOnLayout){ // not supported anymore
32372 //c.el.setStyle("display", "none");
32374 if(north && north.isVisible()){
32375 var b = north.getBox();
32376 var m = north.getMargins();
32377 b.width = w - (m.left+m.right);
32380 centerY = b.height + b.y + m.bottom;
32381 centerH -= centerY;
32382 north.updateBox(this.safeBox(b));
32384 if(south && south.isVisible()){
32385 var b = south.getBox();
32386 var m = south.getMargins();
32387 b.width = w - (m.left+m.right);
32389 var totalHeight = (b.height + m.top + m.bottom);
32390 b.y = h - totalHeight + m.top;
32391 centerH -= totalHeight;
32392 south.updateBox(this.safeBox(b));
32394 if(west && west.isVisible()){
32395 var b = west.getBox();
32396 var m = west.getMargins();
32397 b.height = centerH - (m.top+m.bottom);
32399 b.y = centerY + m.top;
32400 var totalWidth = (b.width + m.left + m.right);
32401 centerX += totalWidth;
32402 centerW -= totalWidth;
32403 west.updateBox(this.safeBox(b));
32405 if(east && east.isVisible()){
32406 var b = east.getBox();
32407 var m = east.getMargins();
32408 b.height = centerH - (m.top+m.bottom);
32409 var totalWidth = (b.width + m.left + m.right);
32410 b.x = w - totalWidth + m.left;
32411 b.y = centerY + m.top;
32412 centerW -= totalWidth;
32413 east.updateBox(this.safeBox(b));
32416 var m = center.getMargins();
32418 x: centerX + m.left,
32419 y: centerY + m.top,
32420 width: centerW - (m.left+m.right),
32421 height: centerH - (m.top+m.bottom)
32423 //if(this.hideOnLayout){
32424 //center.el.setStyle("display", "block");
32426 center.updateBox(this.safeBox(centerBox));
32429 this.fireEvent("layout", this);
32433 safeBox : function(box){
32434 box.width = Math.max(0, box.width);
32435 box.height = Math.max(0, box.height);
32440 * Adds a ContentPanel (or subclass) to this layout.
32441 * @param {String} target The target region key (north, south, east, west or center).
32442 * @param {Roo.ContentPanel} panel The panel to add
32443 * @return {Roo.ContentPanel} The added panel
32445 add : function(target, panel){
32447 target = target.toLowerCase();
32448 return this.regions[target].add(panel);
32452 * Remove a ContentPanel (or subclass) to this layout.
32453 * @param {String} target The target region key (north, south, east, west or center).
32454 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
32455 * @return {Roo.ContentPanel} The removed panel
32457 remove : function(target, panel){
32458 target = target.toLowerCase();
32459 return this.regions[target].remove(panel);
32463 * Searches all regions for a panel with the specified id
32464 * @param {String} panelId
32465 * @return {Roo.ContentPanel} The panel or null if it wasn't found
32467 findPanel : function(panelId){
32468 var rs = this.regions;
32469 for(var target in rs){
32470 if(typeof rs[target] != "function"){
32471 var p = rs[target].getPanel(panelId);
32481 * Searches all regions for a panel with the specified id and activates (shows) it.
32482 * @param {String/ContentPanel} panelId The panels id or the panel itself
32483 * @return {Roo.ContentPanel} The shown panel or null
32485 showPanel : function(panelId) {
32486 var rs = this.regions;
32487 for(var target in rs){
32488 var r = rs[target];
32489 if(typeof r != "function"){
32490 if(r.hasPanel(panelId)){
32491 return r.showPanel(panelId);
32499 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
32500 * @param {Roo.state.Provider} provider (optional) An alternate state provider
32503 restoreState : function(provider){
32505 provider = Roo.state.Manager;
32507 var sm = new Roo.LayoutStateManager();
32508 sm.init(this, provider);
32514 * Adds a xtype elements to the layout.
32518 xtype : 'ContentPanel',
32525 xtype : 'NestedLayoutPanel',
32531 items : [ ... list of content panels or nested layout panels.. ]
32535 * @param {Object} cfg Xtype definition of item to add.
32537 addxtype : function(cfg)
32539 // basically accepts a pannel...
32540 // can accept a layout region..!?!?
32541 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
32544 // theory? children can only be panels??
32546 //if (!cfg.xtype.match(/Panel$/)) {
32551 if (typeof(cfg.region) == 'undefined') {
32552 Roo.log("Failed to add Panel, region was not set");
32556 var region = cfg.region;
32562 xitems = cfg.items;
32569 case 'Content': // ContentPanel (el, cfg)
32570 case 'Scroll': // ContentPanel (el, cfg)
32572 cfg.autoCreate = true;
32573 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
32575 // var el = this.el.createChild();
32576 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
32579 this.add(region, ret);
32583 case 'TreePanel': // our new panel!
32584 cfg.el = this.el.createChild();
32585 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32586 this.add(region, ret);
32591 // create a new Layout (which is a Border Layout...
32593 var clayout = cfg.layout;
32594 clayout.el = this.el.createChild();
32595 clayout.items = clayout.items || [];
32599 // replace this exitems with the clayout ones..
32600 xitems = clayout.items;
32602 // force background off if it's in center...
32603 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
32604 cfg.background = false;
32606 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
32609 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
32610 //console.log('adding nested layout panel ' + cfg.toSource());
32611 this.add(region, ret);
32612 nb = {}; /// find first...
32617 // needs grid and region
32619 //var el = this.getRegion(region).el.createChild();
32621 *var el = this.el.createChild();
32622 // create the grid first...
32623 cfg.grid.container = el;
32624 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
32627 if (region == 'center' && this.active ) {
32628 cfg.background = false;
32631 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
32633 this.add(region, ret);
32635 if (cfg.background) {
32636 // render grid on panel activation (if panel background)
32637 ret.on('activate', function(gp) {
32638 if (!gp.grid.rendered) {
32639 // gp.grid.render(el);
32643 // cfg.grid.render(el);
32649 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
32650 // it was the old xcomponent building that caused this before.
32651 // espeically if border is the top element in the tree.
32661 if (typeof(Roo[cfg.xtype]) != 'undefined') {
32663 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32664 this.add(region, ret);
32668 throw "Can not add '" + cfg.xtype + "' to Border";
32674 this.beginUpdate();
32678 Roo.each(xitems, function(i) {
32679 region = nb && i.region ? i.region : false;
32681 var add = ret.addxtype(i);
32684 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
32685 if (!i.background) {
32686 abn[region] = nb[region] ;
32693 // make the last non-background panel active..
32694 //if (nb) { Roo.log(abn); }
32697 for(var r in abn) {
32698 region = this.getRegion(r);
32700 // tried using nb[r], but it does not work..
32702 region.showPanel(abn[r]);
32713 factory : function(cfg)
32716 var validRegions = Roo.bootstrap.layout.Border.regions;
32718 var target = cfg.region;
32721 var r = Roo.bootstrap.layout;
32725 return new r.North(cfg);
32727 return new r.South(cfg);
32729 return new r.East(cfg);
32731 return new r.West(cfg);
32733 return new r.Center(cfg);
32735 throw 'Layout region "'+target+'" not supported.';
32742 * Ext JS Library 1.1.1
32743 * Copyright(c) 2006-2007, Ext JS, LLC.
32745 * Originally Released Under LGPL - original licence link has changed is not relivant.
32748 * <script type="text/javascript">
32752 * @class Roo.bootstrap.layout.Basic
32753 * @extends Roo.util.Observable
32754 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
32755 * and does not have a titlebar, tabs or any other features. All it does is size and position
32756 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
32757 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
32758 * @cfg {string} region the region that it inhabits..
32759 * @cfg {bool} skipConfig skip config?
32763 Roo.bootstrap.layout.Basic = function(config){
32765 this.mgr = config.mgr;
32767 this.position = config.region;
32769 var skipConfig = config.skipConfig;
32773 * @scope Roo.BasicLayoutRegion
32777 * @event beforeremove
32778 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
32779 * @param {Roo.LayoutRegion} this
32780 * @param {Roo.ContentPanel} panel The panel
32781 * @param {Object} e The cancel event object
32783 "beforeremove" : true,
32785 * @event invalidated
32786 * Fires when the layout for this region is changed.
32787 * @param {Roo.LayoutRegion} this
32789 "invalidated" : true,
32791 * @event visibilitychange
32792 * Fires when this region is shown or hidden
32793 * @param {Roo.LayoutRegion} this
32794 * @param {Boolean} visibility true or false
32796 "visibilitychange" : true,
32798 * @event paneladded
32799 * Fires when a panel is added.
32800 * @param {Roo.LayoutRegion} this
32801 * @param {Roo.ContentPanel} panel The panel
32803 "paneladded" : true,
32805 * @event panelremoved
32806 * Fires when a panel is removed.
32807 * @param {Roo.LayoutRegion} this
32808 * @param {Roo.ContentPanel} panel The panel
32810 "panelremoved" : true,
32812 * @event beforecollapse
32813 * Fires when this region before collapse.
32814 * @param {Roo.LayoutRegion} this
32816 "beforecollapse" : true,
32819 * Fires when this region is collapsed.
32820 * @param {Roo.LayoutRegion} this
32822 "collapsed" : true,
32825 * Fires when this region is expanded.
32826 * @param {Roo.LayoutRegion} this
32831 * Fires when this region is slid into view.
32832 * @param {Roo.LayoutRegion} this
32834 "slideshow" : true,
32837 * Fires when this region slides out of view.
32838 * @param {Roo.LayoutRegion} this
32840 "slidehide" : true,
32842 * @event panelactivated
32843 * Fires when a panel is activated.
32844 * @param {Roo.LayoutRegion} this
32845 * @param {Roo.ContentPanel} panel The activated panel
32847 "panelactivated" : true,
32850 * Fires when the user resizes this region.
32851 * @param {Roo.LayoutRegion} this
32852 * @param {Number} newSize The new size (width for east/west, height for north/south)
32856 /** A collection of panels in this region. @type Roo.util.MixedCollection */
32857 this.panels = new Roo.util.MixedCollection();
32858 this.panels.getKey = this.getPanelId.createDelegate(this);
32860 this.activePanel = null;
32861 // ensure listeners are added...
32863 if (config.listeners || config.events) {
32864 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
32865 listeners : config.listeners || {},
32866 events : config.events || {}
32870 if(skipConfig !== true){
32871 this.applyConfig(config);
32875 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
32877 getPanelId : function(p){
32881 applyConfig : function(config){
32882 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
32883 this.config = config;
32888 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
32889 * the width, for horizontal (north, south) the height.
32890 * @param {Number} newSize The new width or height
32892 resizeTo : function(newSize){
32893 var el = this.el ? this.el :
32894 (this.activePanel ? this.activePanel.getEl() : null);
32896 switch(this.position){
32899 el.setWidth(newSize);
32900 this.fireEvent("resized", this, newSize);
32904 el.setHeight(newSize);
32905 this.fireEvent("resized", this, newSize);
32911 getBox : function(){
32912 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
32915 getMargins : function(){
32916 return this.margins;
32919 updateBox : function(box){
32921 var el = this.activePanel.getEl();
32922 el.dom.style.left = box.x + "px";
32923 el.dom.style.top = box.y + "px";
32924 this.activePanel.setSize(box.width, box.height);
32928 * Returns the container element for this region.
32929 * @return {Roo.Element}
32931 getEl : function(){
32932 return this.activePanel;
32936 * Returns true if this region is currently visible.
32937 * @return {Boolean}
32939 isVisible : function(){
32940 return this.activePanel ? true : false;
32943 setActivePanel : function(panel){
32944 panel = this.getPanel(panel);
32945 if(this.activePanel && this.activePanel != panel){
32946 this.activePanel.setActiveState(false);
32947 this.activePanel.getEl().setLeftTop(-10000,-10000);
32949 this.activePanel = panel;
32950 panel.setActiveState(true);
32952 panel.setSize(this.box.width, this.box.height);
32954 this.fireEvent("panelactivated", this, panel);
32955 this.fireEvent("invalidated");
32959 * Show the specified panel.
32960 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
32961 * @return {Roo.ContentPanel} The shown panel or null
32963 showPanel : function(panel){
32964 panel = this.getPanel(panel);
32966 this.setActivePanel(panel);
32972 * Get the active panel for this region.
32973 * @return {Roo.ContentPanel} The active panel or null
32975 getActivePanel : function(){
32976 return this.activePanel;
32980 * Add the passed ContentPanel(s)
32981 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
32982 * @return {Roo.ContentPanel} The panel added (if only one was added)
32984 add : function(panel){
32985 if(arguments.length > 1){
32986 for(var i = 0, len = arguments.length; i < len; i++) {
32987 this.add(arguments[i]);
32991 if(this.hasPanel(panel)){
32992 this.showPanel(panel);
32995 var el = panel.getEl();
32996 if(el.dom.parentNode != this.mgr.el.dom){
32997 this.mgr.el.dom.appendChild(el.dom);
32999 if(panel.setRegion){
33000 panel.setRegion(this);
33002 this.panels.add(panel);
33003 el.setStyle("position", "absolute");
33004 if(!panel.background){
33005 this.setActivePanel(panel);
33006 if(this.config.initialSize && this.panels.getCount()==1){
33007 this.resizeTo(this.config.initialSize);
33010 this.fireEvent("paneladded", this, panel);
33015 * Returns true if the panel is in this region.
33016 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33017 * @return {Boolean}
33019 hasPanel : function(panel){
33020 if(typeof panel == "object"){ // must be panel obj
33021 panel = panel.getId();
33023 return this.getPanel(panel) ? true : false;
33027 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33028 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33029 * @param {Boolean} preservePanel Overrides the config preservePanel option
33030 * @return {Roo.ContentPanel} The panel that was removed
33032 remove : function(panel, preservePanel){
33033 panel = this.getPanel(panel);
33038 this.fireEvent("beforeremove", this, panel, e);
33039 if(e.cancel === true){
33042 var panelId = panel.getId();
33043 this.panels.removeKey(panelId);
33048 * Returns the panel specified or null if it's not in this region.
33049 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33050 * @return {Roo.ContentPanel}
33052 getPanel : function(id){
33053 if(typeof id == "object"){ // must be panel obj
33056 return this.panels.get(id);
33060 * Returns this regions position (north/south/east/west/center).
33063 getPosition: function(){
33064 return this.position;
33068 * Ext JS Library 1.1.1
33069 * Copyright(c) 2006-2007, Ext JS, LLC.
33071 * Originally Released Under LGPL - original licence link has changed is not relivant.
33074 * <script type="text/javascript">
33078 * @class Roo.bootstrap.layout.Region
33079 * @extends Roo.bootstrap.layout.Basic
33080 * This class represents a region in a layout manager.
33082 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
33083 * @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})
33084 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
33085 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
33086 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
33087 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
33088 * @cfg {String} title The title for the region (overrides panel titles)
33089 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
33090 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
33091 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
33092 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
33093 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
33094 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
33095 * the space available, similar to FireFox 1.5 tabs (defaults to false)
33096 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
33097 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
33098 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
33100 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
33101 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
33102 * @cfg {Boolean} disableTabTips True to disable tab tooltips
33103 * @cfg {Number} width For East/West panels
33104 * @cfg {Number} height For North/South panels
33105 * @cfg {Boolean} split To show the splitter
33106 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
33108 * @cfg {string} cls Extra CSS classes to add to region
33110 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
33111 * @cfg {string} region the region that it inhabits..
33114 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
33115 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
33117 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
33118 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
33119 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
33121 Roo.bootstrap.layout.Region = function(config)
33123 this.applyConfig(config);
33125 var mgr = config.mgr;
33126 var pos = config.region;
33127 config.skipConfig = true;
33128 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
33131 this.onRender(mgr.el);
33134 this.visible = true;
33135 this.collapsed = false;
33136 this.unrendered_panels = [];
33139 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
33141 position: '', // set by wrapper (eg. north/south etc..)
33142 unrendered_panels : null, // unrendered panels.
33143 createBody : function(){
33144 /** This region's body element
33145 * @type Roo.Element */
33146 this.bodyEl = this.el.createChild({
33148 cls: "roo-layout-panel-body tab-content" // bootstrap added...
33152 onRender: function(ctr, pos)
33154 var dh = Roo.DomHelper;
33155 /** This region's container element
33156 * @type Roo.Element */
33157 this.el = dh.append(ctr.dom, {
33159 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
33161 /** This region's title element
33162 * @type Roo.Element */
33164 this.titleEl = dh.append(this.el.dom,
33167 unselectable: "on",
33168 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
33170 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
33171 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
33174 this.titleEl.enableDisplayMode();
33175 /** This region's title text element
33176 * @type HTMLElement */
33177 this.titleTextEl = this.titleEl.dom.firstChild;
33178 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
33180 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
33181 this.closeBtn.enableDisplayMode();
33182 this.closeBtn.on("click", this.closeClicked, this);
33183 this.closeBtn.hide();
33185 this.createBody(this.config);
33186 if(this.config.hideWhenEmpty){
33188 this.on("paneladded", this.validateVisibility, this);
33189 this.on("panelremoved", this.validateVisibility, this);
33191 if(this.autoScroll){
33192 this.bodyEl.setStyle("overflow", "auto");
33194 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
33196 //if(c.titlebar !== false){
33197 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
33198 this.titleEl.hide();
33200 this.titleEl.show();
33201 if(this.config.title){
33202 this.titleTextEl.innerHTML = this.config.title;
33206 if(this.config.collapsed){
33207 this.collapse(true);
33209 if(this.config.hidden){
33213 if (this.unrendered_panels && this.unrendered_panels.length) {
33214 for (var i =0;i< this.unrendered_panels.length; i++) {
33215 this.add(this.unrendered_panels[i]);
33217 this.unrendered_panels = null;
33223 applyConfig : function(c)
33226 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
33227 var dh = Roo.DomHelper;
33228 if(c.titlebar !== false){
33229 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
33230 this.collapseBtn.on("click", this.collapse, this);
33231 this.collapseBtn.enableDisplayMode();
33233 if(c.showPin === true || this.showPin){
33234 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
33235 this.stickBtn.enableDisplayMode();
33236 this.stickBtn.on("click", this.expand, this);
33237 this.stickBtn.hide();
33242 /** This region's collapsed element
33243 * @type Roo.Element */
33246 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
33247 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
33250 if(c.floatable !== false){
33251 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
33252 this.collapsedEl.on("click", this.collapseClick, this);
33255 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
33256 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
33257 id: "message", unselectable: "on", style:{"float":"left"}});
33258 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
33260 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
33261 this.expandBtn.on("click", this.expand, this);
33265 if(this.collapseBtn){
33266 this.collapseBtn.setVisible(c.collapsible == true);
33269 this.cmargins = c.cmargins || this.cmargins ||
33270 (this.position == "west" || this.position == "east" ?
33271 {top: 0, left: 2, right:2, bottom: 0} :
33272 {top: 2, left: 0, right:0, bottom: 2});
33274 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33277 this.bottomTabs = c.tabPosition != "top";
33279 this.autoScroll = c.autoScroll || false;
33284 this.duration = c.duration || .30;
33285 this.slideDuration = c.slideDuration || .45;
33290 * Returns true if this region is currently visible.
33291 * @return {Boolean}
33293 isVisible : function(){
33294 return this.visible;
33298 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
33299 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
33301 //setCollapsedTitle : function(title){
33302 // title = title || " ";
33303 // if(this.collapsedTitleTextEl){
33304 // this.collapsedTitleTextEl.innerHTML = title;
33308 getBox : function(){
33310 // if(!this.collapsed){
33311 b = this.el.getBox(false, true);
33313 // b = this.collapsedEl.getBox(false, true);
33318 getMargins : function(){
33319 return this.margins;
33320 //return this.collapsed ? this.cmargins : this.margins;
33323 highlight : function(){
33324 this.el.addClass("x-layout-panel-dragover");
33327 unhighlight : function(){
33328 this.el.removeClass("x-layout-panel-dragover");
33331 updateBox : function(box)
33333 if (!this.bodyEl) {
33334 return; // not rendered yet..
33338 if(!this.collapsed){
33339 this.el.dom.style.left = box.x + "px";
33340 this.el.dom.style.top = box.y + "px";
33341 this.updateBody(box.width, box.height);
33343 this.collapsedEl.dom.style.left = box.x + "px";
33344 this.collapsedEl.dom.style.top = box.y + "px";
33345 this.collapsedEl.setSize(box.width, box.height);
33348 this.tabs.autoSizeTabs();
33352 updateBody : function(w, h)
33355 this.el.setWidth(w);
33356 w -= this.el.getBorderWidth("rl");
33357 if(this.config.adjustments){
33358 w += this.config.adjustments[0];
33361 if(h !== null && h > 0){
33362 this.el.setHeight(h);
33363 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
33364 h -= this.el.getBorderWidth("tb");
33365 if(this.config.adjustments){
33366 h += this.config.adjustments[1];
33368 this.bodyEl.setHeight(h);
33370 h = this.tabs.syncHeight(h);
33373 if(this.panelSize){
33374 w = w !== null ? w : this.panelSize.width;
33375 h = h !== null ? h : this.panelSize.height;
33377 if(this.activePanel){
33378 var el = this.activePanel.getEl();
33379 w = w !== null ? w : el.getWidth();
33380 h = h !== null ? h : el.getHeight();
33381 this.panelSize = {width: w, height: h};
33382 this.activePanel.setSize(w, h);
33384 if(Roo.isIE && this.tabs){
33385 this.tabs.el.repaint();
33390 * Returns the container element for this region.
33391 * @return {Roo.Element}
33393 getEl : function(){
33398 * Hides this region.
33401 //if(!this.collapsed){
33402 this.el.dom.style.left = "-2000px";
33405 // this.collapsedEl.dom.style.left = "-2000px";
33406 // this.collapsedEl.hide();
33408 this.visible = false;
33409 this.fireEvent("visibilitychange", this, false);
33413 * Shows this region if it was previously hidden.
33416 //if(!this.collapsed){
33419 // this.collapsedEl.show();
33421 this.visible = true;
33422 this.fireEvent("visibilitychange", this, true);
33425 closeClicked : function(){
33426 if(this.activePanel){
33427 this.remove(this.activePanel);
33431 collapseClick : function(e){
33433 e.stopPropagation();
33436 e.stopPropagation();
33442 * Collapses this region.
33443 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
33446 collapse : function(skipAnim, skipCheck = false){
33447 if(this.collapsed) {
33451 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
33453 this.collapsed = true;
33455 this.split.el.hide();
33457 if(this.config.animate && skipAnim !== true){
33458 this.fireEvent("invalidated", this);
33459 this.animateCollapse();
33461 this.el.setLocation(-20000,-20000);
33463 this.collapsedEl.show();
33464 this.fireEvent("collapsed", this);
33465 this.fireEvent("invalidated", this);
33471 animateCollapse : function(){
33476 * Expands this region if it was previously collapsed.
33477 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
33478 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
33481 expand : function(e, skipAnim){
33483 e.stopPropagation();
33485 if(!this.collapsed || this.el.hasActiveFx()) {
33489 this.afterSlideIn();
33492 this.collapsed = false;
33493 if(this.config.animate && skipAnim !== true){
33494 this.animateExpand();
33498 this.split.el.show();
33500 this.collapsedEl.setLocation(-2000,-2000);
33501 this.collapsedEl.hide();
33502 this.fireEvent("invalidated", this);
33503 this.fireEvent("expanded", this);
33507 animateExpand : function(){
33511 initTabs : function()
33513 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
33515 var ts = new Roo.bootstrap.panel.Tabs({
33516 el: this.bodyEl.dom,
33517 tabPosition: this.bottomTabs ? 'bottom' : 'top',
33518 disableTooltips: this.config.disableTabTips,
33519 toolbar : this.config.toolbar
33522 if(this.config.hideTabs){
33523 ts.stripWrap.setDisplayed(false);
33526 ts.resizeTabs = this.config.resizeTabs === true;
33527 ts.minTabWidth = this.config.minTabWidth || 40;
33528 ts.maxTabWidth = this.config.maxTabWidth || 250;
33529 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
33530 ts.monitorResize = false;
33531 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
33532 ts.bodyEl.addClass('roo-layout-tabs-body');
33533 this.panels.each(this.initPanelAsTab, this);
33536 initPanelAsTab : function(panel){
33537 var ti = this.tabs.addTab(
33541 this.config.closeOnTab && panel.isClosable(),
33544 if(panel.tabTip !== undefined){
33545 ti.setTooltip(panel.tabTip);
33547 ti.on("activate", function(){
33548 this.setActivePanel(panel);
33551 if(this.config.closeOnTab){
33552 ti.on("beforeclose", function(t, e){
33554 this.remove(panel);
33558 panel.tabItem = ti;
33563 updatePanelTitle : function(panel, title)
33565 if(this.activePanel == panel){
33566 this.updateTitle(title);
33569 var ti = this.tabs.getTab(panel.getEl().id);
33571 if(panel.tabTip !== undefined){
33572 ti.setTooltip(panel.tabTip);
33577 updateTitle : function(title){
33578 if(this.titleTextEl && !this.config.title){
33579 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
33583 setActivePanel : function(panel)
33585 panel = this.getPanel(panel);
33586 if(this.activePanel && this.activePanel != panel){
33587 this.activePanel.setActiveState(false);
33589 this.activePanel = panel;
33590 panel.setActiveState(true);
33591 if(this.panelSize){
33592 panel.setSize(this.panelSize.width, this.panelSize.height);
33595 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
33597 this.updateTitle(panel.getTitle());
33599 this.fireEvent("invalidated", this);
33601 this.fireEvent("panelactivated", this, panel);
33605 * Shows the specified panel.
33606 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
33607 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
33609 showPanel : function(panel)
33611 panel = this.getPanel(panel);
33614 var tab = this.tabs.getTab(panel.getEl().id);
33615 if(tab.isHidden()){
33616 this.tabs.unhideTab(tab.id);
33620 this.setActivePanel(panel);
33627 * Get the active panel for this region.
33628 * @return {Roo.ContentPanel} The active panel or null
33630 getActivePanel : function(){
33631 return this.activePanel;
33634 validateVisibility : function(){
33635 if(this.panels.getCount() < 1){
33636 this.updateTitle(" ");
33637 this.closeBtn.hide();
33640 if(!this.isVisible()){
33647 * Adds the passed ContentPanel(s) to this region.
33648 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33649 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
33651 add : function(panel)
33653 if(arguments.length > 1){
33654 for(var i = 0, len = arguments.length; i < len; i++) {
33655 this.add(arguments[i]);
33660 // if we have not been rendered yet, then we can not really do much of this..
33661 if (!this.bodyEl) {
33662 this.unrendered_panels.push(panel);
33669 if(this.hasPanel(panel)){
33670 this.showPanel(panel);
33673 panel.setRegion(this);
33674 this.panels.add(panel);
33675 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
33676 // sinle panel - no tab...?? would it not be better to render it with the tabs,
33677 // and hide them... ???
33678 this.bodyEl.dom.appendChild(panel.getEl().dom);
33679 if(panel.background !== true){
33680 this.setActivePanel(panel);
33682 this.fireEvent("paneladded", this, panel);
33689 this.initPanelAsTab(panel);
33693 if(panel.background !== true){
33694 this.tabs.activate(panel.getEl().id);
33696 this.fireEvent("paneladded", this, panel);
33701 * Hides the tab for the specified panel.
33702 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33704 hidePanel : function(panel){
33705 if(this.tabs && (panel = this.getPanel(panel))){
33706 this.tabs.hideTab(panel.getEl().id);
33711 * Unhides the tab for a previously hidden panel.
33712 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33714 unhidePanel : function(panel){
33715 if(this.tabs && (panel = this.getPanel(panel))){
33716 this.tabs.unhideTab(panel.getEl().id);
33720 clearPanels : function(){
33721 while(this.panels.getCount() > 0){
33722 this.remove(this.panels.first());
33727 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33728 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33729 * @param {Boolean} preservePanel Overrides the config preservePanel option
33730 * @return {Roo.ContentPanel} The panel that was removed
33732 remove : function(panel, preservePanel)
33734 panel = this.getPanel(panel);
33739 this.fireEvent("beforeremove", this, panel, e);
33740 if(e.cancel === true){
33743 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
33744 var panelId = panel.getId();
33745 this.panels.removeKey(panelId);
33747 document.body.appendChild(panel.getEl().dom);
33750 this.tabs.removeTab(panel.getEl().id);
33751 }else if (!preservePanel){
33752 this.bodyEl.dom.removeChild(panel.getEl().dom);
33754 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
33755 var p = this.panels.first();
33756 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
33757 tempEl.appendChild(p.getEl().dom);
33758 this.bodyEl.update("");
33759 this.bodyEl.dom.appendChild(p.getEl().dom);
33761 this.updateTitle(p.getTitle());
33763 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
33764 this.setActivePanel(p);
33766 panel.setRegion(null);
33767 if(this.activePanel == panel){
33768 this.activePanel = null;
33770 if(this.config.autoDestroy !== false && preservePanel !== true){
33771 try{panel.destroy();}catch(e){}
33773 this.fireEvent("panelremoved", this, panel);
33778 * Returns the TabPanel component used by this region
33779 * @return {Roo.TabPanel}
33781 getTabs : function(){
33785 createTool : function(parentEl, className){
33786 var btn = Roo.DomHelper.append(parentEl, {
33788 cls: "x-layout-tools-button",
33791 cls: "roo-layout-tools-button-inner " + className,
33795 btn.addClassOnOver("roo-layout-tools-button-over");
33800 * Ext JS Library 1.1.1
33801 * Copyright(c) 2006-2007, Ext JS, LLC.
33803 * Originally Released Under LGPL - original licence link has changed is not relivant.
33806 * <script type="text/javascript">
33812 * @class Roo.SplitLayoutRegion
33813 * @extends Roo.LayoutRegion
33814 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
33816 Roo.bootstrap.layout.Split = function(config){
33817 this.cursor = config.cursor;
33818 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
33821 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
33823 splitTip : "Drag to resize.",
33824 collapsibleSplitTip : "Drag to resize. Double click to hide.",
33825 useSplitTips : false,
33827 applyConfig : function(config){
33828 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
33831 onRender : function(ctr,pos) {
33833 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
33834 if(!this.config.split){
33839 var splitEl = Roo.DomHelper.append(ctr.dom, {
33841 id: this.el.id + "-split",
33842 cls: "roo-layout-split roo-layout-split-"+this.position,
33845 /** The SplitBar for this region
33846 * @type Roo.SplitBar */
33847 // does not exist yet...
33848 Roo.log([this.position, this.orientation]);
33850 this.split = new Roo.bootstrap.SplitBar({
33851 dragElement : splitEl,
33852 resizingElement: this.el,
33853 orientation : this.orientation
33856 this.split.on("moved", this.onSplitMove, this);
33857 this.split.useShim = this.config.useShim === true;
33858 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
33859 if(this.useSplitTips){
33860 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
33862 //if(config.collapsible){
33863 // this.split.el.on("dblclick", this.collapse, this);
33866 if(typeof this.config.minSize != "undefined"){
33867 this.split.minSize = this.config.minSize;
33869 if(typeof this.config.maxSize != "undefined"){
33870 this.split.maxSize = this.config.maxSize;
33872 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
33873 this.hideSplitter();
33878 getHMaxSize : function(){
33879 var cmax = this.config.maxSize || 10000;
33880 var center = this.mgr.getRegion("center");
33881 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
33884 getVMaxSize : function(){
33885 var cmax = this.config.maxSize || 10000;
33886 var center = this.mgr.getRegion("center");
33887 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
33890 onSplitMove : function(split, newSize){
33891 this.fireEvent("resized", this, newSize);
33895 * Returns the {@link Roo.SplitBar} for this region.
33896 * @return {Roo.SplitBar}
33898 getSplitBar : function(){
33903 this.hideSplitter();
33904 Roo.bootstrap.layout.Split.superclass.hide.call(this);
33907 hideSplitter : function(){
33909 this.split.el.setLocation(-2000,-2000);
33910 this.split.el.hide();
33916 this.split.el.show();
33918 Roo.bootstrap.layout.Split.superclass.show.call(this);
33921 beforeSlide: function(){
33922 if(Roo.isGecko){// firefox overflow auto bug workaround
33923 this.bodyEl.clip();
33925 this.tabs.bodyEl.clip();
33927 if(this.activePanel){
33928 this.activePanel.getEl().clip();
33930 if(this.activePanel.beforeSlide){
33931 this.activePanel.beforeSlide();
33937 afterSlide : function(){
33938 if(Roo.isGecko){// firefox overflow auto bug workaround
33939 this.bodyEl.unclip();
33941 this.tabs.bodyEl.unclip();
33943 if(this.activePanel){
33944 this.activePanel.getEl().unclip();
33945 if(this.activePanel.afterSlide){
33946 this.activePanel.afterSlide();
33952 initAutoHide : function(){
33953 if(this.autoHide !== false){
33954 if(!this.autoHideHd){
33955 var st = new Roo.util.DelayedTask(this.slideIn, this);
33956 this.autoHideHd = {
33957 "mouseout": function(e){
33958 if(!e.within(this.el, true)){
33962 "mouseover" : function(e){
33968 this.el.on(this.autoHideHd);
33972 clearAutoHide : function(){
33973 if(this.autoHide !== false){
33974 this.el.un("mouseout", this.autoHideHd.mouseout);
33975 this.el.un("mouseover", this.autoHideHd.mouseover);
33979 clearMonitor : function(){
33980 Roo.get(document).un("click", this.slideInIf, this);
33983 // these names are backwards but not changed for compat
33984 slideOut : function(){
33985 if(this.isSlid || this.el.hasActiveFx()){
33988 this.isSlid = true;
33989 if(this.collapseBtn){
33990 this.collapseBtn.hide();
33992 this.closeBtnState = this.closeBtn.getStyle('display');
33993 this.closeBtn.hide();
33995 this.stickBtn.show();
33998 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
33999 this.beforeSlide();
34000 this.el.setStyle("z-index", 10001);
34001 this.el.slideIn(this.getSlideAnchor(), {
34002 callback: function(){
34004 this.initAutoHide();
34005 Roo.get(document).on("click", this.slideInIf, this);
34006 this.fireEvent("slideshow", this);
34013 afterSlideIn : function(){
34014 this.clearAutoHide();
34015 this.isSlid = false;
34016 this.clearMonitor();
34017 this.el.setStyle("z-index", "");
34018 if(this.collapseBtn){
34019 this.collapseBtn.show();
34021 this.closeBtn.setStyle('display', this.closeBtnState);
34023 this.stickBtn.hide();
34025 this.fireEvent("slidehide", this);
34028 slideIn : function(cb){
34029 if(!this.isSlid || this.el.hasActiveFx()){
34033 this.isSlid = false;
34034 this.beforeSlide();
34035 this.el.slideOut(this.getSlideAnchor(), {
34036 callback: function(){
34037 this.el.setLeftTop(-10000, -10000);
34039 this.afterSlideIn();
34047 slideInIf : function(e){
34048 if(!e.within(this.el)){
34053 animateCollapse : function(){
34054 this.beforeSlide();
34055 this.el.setStyle("z-index", 20000);
34056 var anchor = this.getSlideAnchor();
34057 this.el.slideOut(anchor, {
34058 callback : function(){
34059 this.el.setStyle("z-index", "");
34060 this.collapsedEl.slideIn(anchor, {duration:.3});
34062 this.el.setLocation(-10000,-10000);
34064 this.fireEvent("collapsed", this);
34071 animateExpand : function(){
34072 this.beforeSlide();
34073 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
34074 this.el.setStyle("z-index", 20000);
34075 this.collapsedEl.hide({
34078 this.el.slideIn(this.getSlideAnchor(), {
34079 callback : function(){
34080 this.el.setStyle("z-index", "");
34083 this.split.el.show();
34085 this.fireEvent("invalidated", this);
34086 this.fireEvent("expanded", this);
34114 getAnchor : function(){
34115 return this.anchors[this.position];
34118 getCollapseAnchor : function(){
34119 return this.canchors[this.position];
34122 getSlideAnchor : function(){
34123 return this.sanchors[this.position];
34126 getAlignAdj : function(){
34127 var cm = this.cmargins;
34128 switch(this.position){
34144 getExpandAdj : function(){
34145 var c = this.collapsedEl, cm = this.cmargins;
34146 switch(this.position){
34148 return [-(cm.right+c.getWidth()+cm.left), 0];
34151 return [cm.right+c.getWidth()+cm.left, 0];
34154 return [0, -(cm.top+cm.bottom+c.getHeight())];
34157 return [0, cm.top+cm.bottom+c.getHeight()];
34163 * Ext JS Library 1.1.1
34164 * Copyright(c) 2006-2007, Ext JS, LLC.
34166 * Originally Released Under LGPL - original licence link has changed is not relivant.
34169 * <script type="text/javascript">
34172 * These classes are private internal classes
34174 Roo.bootstrap.layout.Center = function(config){
34175 config.region = "center";
34176 Roo.bootstrap.layout.Region.call(this, config);
34177 this.visible = true;
34178 this.minWidth = config.minWidth || 20;
34179 this.minHeight = config.minHeight || 20;
34182 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
34184 // center panel can't be hidden
34188 // center panel can't be hidden
34191 getMinWidth: function(){
34192 return this.minWidth;
34195 getMinHeight: function(){
34196 return this.minHeight;
34209 Roo.bootstrap.layout.North = function(config)
34211 config.region = 'north';
34212 config.cursor = 'n-resize';
34214 Roo.bootstrap.layout.Split.call(this, config);
34218 this.split.placement = Roo.bootstrap.SplitBar.TOP;
34219 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
34220 this.split.el.addClass("roo-layout-split-v");
34222 var size = config.initialSize || config.height;
34223 if(typeof size != "undefined"){
34224 this.el.setHeight(size);
34227 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
34229 orientation: Roo.bootstrap.SplitBar.VERTICAL,
34233 getBox : function(){
34234 if(this.collapsed){
34235 return this.collapsedEl.getBox();
34237 var box = this.el.getBox();
34239 box.height += this.split.el.getHeight();
34244 updateBox : function(box){
34245 if(this.split && !this.collapsed){
34246 box.height -= this.split.el.getHeight();
34247 this.split.el.setLeft(box.x);
34248 this.split.el.setTop(box.y+box.height);
34249 this.split.el.setWidth(box.width);
34251 if(this.collapsed){
34252 this.updateBody(box.width, null);
34254 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34262 Roo.bootstrap.layout.South = function(config){
34263 config.region = 'south';
34264 config.cursor = 's-resize';
34265 Roo.bootstrap.layout.Split.call(this, config);
34267 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
34268 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
34269 this.split.el.addClass("roo-layout-split-v");
34271 var size = config.initialSize || config.height;
34272 if(typeof size != "undefined"){
34273 this.el.setHeight(size);
34277 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
34278 orientation: Roo.bootstrap.SplitBar.VERTICAL,
34279 getBox : function(){
34280 if(this.collapsed){
34281 return this.collapsedEl.getBox();
34283 var box = this.el.getBox();
34285 var sh = this.split.el.getHeight();
34292 updateBox : function(box){
34293 if(this.split && !this.collapsed){
34294 var sh = this.split.el.getHeight();
34297 this.split.el.setLeft(box.x);
34298 this.split.el.setTop(box.y-sh);
34299 this.split.el.setWidth(box.width);
34301 if(this.collapsed){
34302 this.updateBody(box.width, null);
34304 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34308 Roo.bootstrap.layout.East = function(config){
34309 config.region = "east";
34310 config.cursor = "e-resize";
34311 Roo.bootstrap.layout.Split.call(this, config);
34313 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
34314 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
34315 this.split.el.addClass("roo-layout-split-h");
34317 var size = config.initialSize || config.width;
34318 if(typeof size != "undefined"){
34319 this.el.setWidth(size);
34322 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
34323 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
34324 getBox : function(){
34325 if(this.collapsed){
34326 return this.collapsedEl.getBox();
34328 var box = this.el.getBox();
34330 var sw = this.split.el.getWidth();
34337 updateBox : function(box){
34338 if(this.split && !this.collapsed){
34339 var sw = this.split.el.getWidth();
34341 this.split.el.setLeft(box.x);
34342 this.split.el.setTop(box.y);
34343 this.split.el.setHeight(box.height);
34346 if(this.collapsed){
34347 this.updateBody(null, box.height);
34349 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34353 Roo.bootstrap.layout.West = function(config){
34354 config.region = "west";
34355 config.cursor = "w-resize";
34357 Roo.bootstrap.layout.Split.call(this, config);
34359 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
34360 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
34361 this.split.el.addClass("roo-layout-split-h");
34365 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
34366 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
34368 onRender: function(ctr, pos)
34370 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
34371 var size = this.config.initialSize || this.config.width;
34372 if(typeof size != "undefined"){
34373 this.el.setWidth(size);
34377 getBox : function(){
34378 if(this.collapsed){
34379 return this.collapsedEl.getBox();
34381 var box = this.el.getBox();
34383 box.width += this.split.el.getWidth();
34388 updateBox : function(box){
34389 if(this.split && !this.collapsed){
34390 var sw = this.split.el.getWidth();
34392 this.split.el.setLeft(box.x+box.width);
34393 this.split.el.setTop(box.y);
34394 this.split.el.setHeight(box.height);
34396 if(this.collapsed){
34397 this.updateBody(null, box.height);
34399 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34402 Roo.namespace("Roo.bootstrap.panel");/*
34404 * Ext JS Library 1.1.1
34405 * Copyright(c) 2006-2007, Ext JS, LLC.
34407 * Originally Released Under LGPL - original licence link has changed is not relivant.
34410 * <script type="text/javascript">
34413 * @class Roo.ContentPanel
34414 * @extends Roo.util.Observable
34415 * A basic ContentPanel element.
34416 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
34417 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
34418 * @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
34419 * @cfg {Boolean} closable True if the panel can be closed/removed
34420 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
34421 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
34422 * @cfg {Toolbar} toolbar A toolbar for this panel
34423 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
34424 * @cfg {String} title The title for this panel
34425 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
34426 * @cfg {String} url Calls {@link #setUrl} with this value
34427 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
34428 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
34429 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
34430 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
34431 * @cfg {Boolean} badges render the badges
34434 * Create a new ContentPanel.
34435 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
34436 * @param {String/Object} config A string to set only the title or a config object
34437 * @param {String} content (optional) Set the HTML content for this panel
34438 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
34440 Roo.bootstrap.panel.Content = function( config){
34442 this.tpl = config.tpl || false;
34444 var el = config.el;
34445 var content = config.content;
34447 if(config.autoCreate){ // xtype is available if this is called from factory
34450 this.el = Roo.get(el);
34451 if(!this.el && config && config.autoCreate){
34452 if(typeof config.autoCreate == "object"){
34453 if(!config.autoCreate.id){
34454 config.autoCreate.id = config.id||el;
34456 this.el = Roo.DomHelper.append(document.body,
34457 config.autoCreate, true);
34459 var elcfg = { tag: "div",
34460 cls: "roo-layout-inactive-content",
34464 elcfg.html = config.html;
34468 this.el = Roo.DomHelper.append(document.body, elcfg , true);
34471 this.closable = false;
34472 this.loaded = false;
34473 this.active = false;
34476 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
34478 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
34480 this.wrapEl = this.el; //this.el.wrap();
34482 if (config.toolbar.items) {
34483 ti = config.toolbar.items ;
34484 delete config.toolbar.items ;
34488 this.toolbar.render(this.wrapEl, 'before');
34489 for(var i =0;i < ti.length;i++) {
34490 // Roo.log(['add child', items[i]]);
34491 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
34493 this.toolbar.items = nitems;
34494 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
34495 delete config.toolbar;
34499 // xtype created footer. - not sure if will work as we normally have to render first..
34500 if (this.footer && !this.footer.el && this.footer.xtype) {
34501 if (!this.wrapEl) {
34502 this.wrapEl = this.el.wrap();
34505 this.footer.container = this.wrapEl.createChild();
34507 this.footer = Roo.factory(this.footer, Roo);
34512 if(typeof config == "string"){
34513 this.title = config;
34515 Roo.apply(this, config);
34519 this.resizeEl = Roo.get(this.resizeEl, true);
34521 this.resizeEl = this.el;
34523 // handle view.xtype
34531 * Fires when this panel is activated.
34532 * @param {Roo.ContentPanel} this
34536 * @event deactivate
34537 * Fires when this panel is activated.
34538 * @param {Roo.ContentPanel} this
34540 "deactivate" : true,
34544 * Fires when this panel is resized if fitToFrame is true.
34545 * @param {Roo.ContentPanel} this
34546 * @param {Number} width The width after any component adjustments
34547 * @param {Number} height The height after any component adjustments
34553 * Fires when this tab is created
34554 * @param {Roo.ContentPanel} this
34565 if(this.autoScroll){
34566 this.resizeEl.setStyle("overflow", "auto");
34568 // fix randome scrolling
34569 //this.el.on('scroll', function() {
34570 // Roo.log('fix random scolling');
34571 // this.scrollTo('top',0);
34574 content = content || this.content;
34576 this.setContent(content);
34578 if(config && config.url){
34579 this.setUrl(this.url, this.params, this.loadOnce);
34584 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
34586 if (this.view && typeof(this.view.xtype) != 'undefined') {
34587 this.view.el = this.el.appendChild(document.createElement("div"));
34588 this.view = Roo.factory(this.view);
34589 this.view.render && this.view.render(false, '');
34593 this.fireEvent('render', this);
34596 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
34600 setRegion : function(region){
34601 this.region = region;
34602 this.setActiveClass(region && !this.background);
34606 setActiveClass: function(state)
34609 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
34610 this.el.setStyle('position','relative');
34612 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
34613 this.el.setStyle('position', 'absolute');
34618 * Returns the toolbar for this Panel if one was configured.
34619 * @return {Roo.Toolbar}
34621 getToolbar : function(){
34622 return this.toolbar;
34625 setActiveState : function(active)
34627 this.active = active;
34628 this.setActiveClass(active);
34630 this.fireEvent("deactivate", this);
34632 this.fireEvent("activate", this);
34636 * Updates this panel's element
34637 * @param {String} content The new content
34638 * @param {Boolean} loadScripts (optional) true to look for and process scripts
34640 setContent : function(content, loadScripts){
34641 this.el.update(content, loadScripts);
34644 ignoreResize : function(w, h){
34645 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
34648 this.lastSize = {width: w, height: h};
34653 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
34654 * @return {Roo.UpdateManager} The UpdateManager
34656 getUpdateManager : function(){
34657 return this.el.getUpdateManager();
34660 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
34661 * @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:
34664 url: "your-url.php",
34665 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
34666 callback: yourFunction,
34667 scope: yourObject, //(optional scope)
34670 text: "Loading...",
34675 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
34676 * 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.
34677 * @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}
34678 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
34679 * @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.
34680 * @return {Roo.ContentPanel} this
34683 var um = this.el.getUpdateManager();
34684 um.update.apply(um, arguments);
34690 * 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.
34691 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
34692 * @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)
34693 * @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)
34694 * @return {Roo.UpdateManager} The UpdateManager
34696 setUrl : function(url, params, loadOnce){
34697 if(this.refreshDelegate){
34698 this.removeListener("activate", this.refreshDelegate);
34700 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
34701 this.on("activate", this.refreshDelegate);
34702 return this.el.getUpdateManager();
34705 _handleRefresh : function(url, params, loadOnce){
34706 if(!loadOnce || !this.loaded){
34707 var updater = this.el.getUpdateManager();
34708 updater.update(url, params, this._setLoaded.createDelegate(this));
34712 _setLoaded : function(){
34713 this.loaded = true;
34717 * Returns this panel's id
34720 getId : function(){
34725 * Returns this panel's element - used by regiosn to add.
34726 * @return {Roo.Element}
34728 getEl : function(){
34729 return this.wrapEl || this.el;
34734 adjustForComponents : function(width, height)
34736 //Roo.log('adjustForComponents ');
34737 if(this.resizeEl != this.el){
34738 width -= this.el.getFrameWidth('lr');
34739 height -= this.el.getFrameWidth('tb');
34742 var te = this.toolbar.getEl();
34743 height -= te.getHeight();
34744 te.setWidth(width);
34747 var te = this.footer.getEl();
34748 Roo.log("footer:" + te.getHeight());
34750 height -= te.getHeight();
34751 te.setWidth(width);
34755 if(this.adjustments){
34756 width += this.adjustments[0];
34757 height += this.adjustments[1];
34759 return {"width": width, "height": height};
34762 setSize : function(width, height){
34763 if(this.fitToFrame && !this.ignoreResize(width, height)){
34764 if(this.fitContainer && this.resizeEl != this.el){
34765 this.el.setSize(width, height);
34767 var size = this.adjustForComponents(width, height);
34768 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
34769 this.fireEvent('resize', this, size.width, size.height);
34774 * Returns this panel's title
34777 getTitle : function(){
34782 * Set this panel's title
34783 * @param {String} title
34785 setTitle : function(title){
34786 this.title = title;
34788 this.region.updatePanelTitle(this, title);
34793 * Returns true is this panel was configured to be closable
34794 * @return {Boolean}
34796 isClosable : function(){
34797 return this.closable;
34800 beforeSlide : function(){
34802 this.resizeEl.clip();
34805 afterSlide : function(){
34807 this.resizeEl.unclip();
34811 * Force a content refresh from the URL specified in the {@link #setUrl} method.
34812 * Will fail silently if the {@link #setUrl} method has not been called.
34813 * This does not activate the panel, just updates its content.
34815 refresh : function(){
34816 if(this.refreshDelegate){
34817 this.loaded = false;
34818 this.refreshDelegate();
34823 * Destroys this panel
34825 destroy : function(){
34826 this.el.removeAllListeners();
34827 var tempEl = document.createElement("span");
34828 tempEl.appendChild(this.el.dom);
34829 tempEl.innerHTML = "";
34835 * form - if the content panel contains a form - this is a reference to it.
34836 * @type {Roo.form.Form}
34840 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
34841 * This contains a reference to it.
34847 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
34857 * @param {Object} cfg Xtype definition of item to add.
34861 getChildContainer: function () {
34862 return this.getEl();
34867 var ret = new Roo.factory(cfg);
34872 if (cfg.xtype.match(/^Form$/)) {
34875 //if (this.footer) {
34876 // el = this.footer.container.insertSibling(false, 'before');
34878 el = this.el.createChild();
34881 this.form = new Roo.form.Form(cfg);
34884 if ( this.form.allItems.length) {
34885 this.form.render(el.dom);
34889 // should only have one of theses..
34890 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
34891 // views.. should not be just added - used named prop 'view''
34893 cfg.el = this.el.appendChild(document.createElement("div"));
34896 var ret = new Roo.factory(cfg);
34898 ret.render && ret.render(false, ''); // render blank..
34908 * @class Roo.bootstrap.panel.Grid
34909 * @extends Roo.bootstrap.panel.Content
34911 * Create a new GridPanel.
34912 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
34913 * @param {Object} config A the config object
34919 Roo.bootstrap.panel.Grid = function(config)
34923 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
34924 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
34926 config.el = this.wrapper;
34927 //this.el = this.wrapper;
34929 if (config.container) {
34930 // ctor'ed from a Border/panel.grid
34933 this.wrapper.setStyle("overflow", "hidden");
34934 this.wrapper.addClass('roo-grid-container');
34939 if(config.toolbar){
34940 var tool_el = this.wrapper.createChild();
34941 this.toolbar = Roo.factory(config.toolbar);
34943 if (config.toolbar.items) {
34944 ti = config.toolbar.items ;
34945 delete config.toolbar.items ;
34949 this.toolbar.render(tool_el);
34950 for(var i =0;i < ti.length;i++) {
34951 // Roo.log(['add child', items[i]]);
34952 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
34954 this.toolbar.items = nitems;
34956 delete config.toolbar;
34959 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
34960 config.grid.scrollBody = true;;
34961 config.grid.monitorWindowResize = false; // turn off autosizing
34962 config.grid.autoHeight = false;
34963 config.grid.autoWidth = false;
34965 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
34967 if (config.background) {
34968 // render grid on panel activation (if panel background)
34969 this.on('activate', function(gp) {
34970 if (!gp.grid.rendered) {
34971 gp.grid.render(this.wrapper);
34972 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
34977 this.grid.render(this.wrapper);
34978 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
34981 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
34982 // ??? needed ??? config.el = this.wrapper;
34987 // xtype created footer. - not sure if will work as we normally have to render first..
34988 if (this.footer && !this.footer.el && this.footer.xtype) {
34990 var ctr = this.grid.getView().getFooterPanel(true);
34991 this.footer.dataSource = this.grid.dataSource;
34992 this.footer = Roo.factory(this.footer, Roo);
34993 this.footer.render(ctr);
35003 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
35004 getId : function(){
35005 return this.grid.id;
35009 * Returns the grid for this panel
35010 * @return {Roo.bootstrap.Table}
35012 getGrid : function(){
35016 setSize : function(width, height){
35017 if(!this.ignoreResize(width, height)){
35018 var grid = this.grid;
35019 var size = this.adjustForComponents(width, height);
35020 var gridel = grid.getGridEl();
35021 gridel.setSize(size.width, size.height);
35023 var thd = grid.getGridEl().select('thead',true).first();
35024 var tbd = grid.getGridEl().select('tbody', true).first();
35026 tbd.setSize(width, height - thd.getHeight());
35035 beforeSlide : function(){
35036 this.grid.getView().scroller.clip();
35039 afterSlide : function(){
35040 this.grid.getView().scroller.unclip();
35043 destroy : function(){
35044 this.grid.destroy();
35046 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
35051 * @class Roo.bootstrap.panel.Nest
35052 * @extends Roo.bootstrap.panel.Content
35054 * Create a new Panel, that can contain a layout.Border.
35057 * @param {Roo.BorderLayout} layout The layout for this panel
35058 * @param {String/Object} config A string to set only the title or a config object
35060 Roo.bootstrap.panel.Nest = function(config)
35062 // construct with only one argument..
35063 /* FIXME - implement nicer consturctors
35064 if (layout.layout) {
35066 layout = config.layout;
35067 delete config.layout;
35069 if (layout.xtype && !layout.getEl) {
35070 // then layout needs constructing..
35071 layout = Roo.factory(layout, Roo);
35075 config.el = config.layout.getEl();
35077 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
35079 config.layout.monitorWindowResize = false; // turn off autosizing
35080 this.layout = config.layout;
35081 this.layout.getEl().addClass("roo-layout-nested-layout");
35088 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
35090 setSize : function(width, height){
35091 if(!this.ignoreResize(width, height)){
35092 var size = this.adjustForComponents(width, height);
35093 var el = this.layout.getEl();
35094 if (size.height < 1) {
35095 el.setWidth(size.width);
35097 el.setSize(size.width, size.height);
35099 var touch = el.dom.offsetWidth;
35100 this.layout.layout();
35101 // ie requires a double layout on the first pass
35102 if(Roo.isIE && !this.initialized){
35103 this.initialized = true;
35104 this.layout.layout();
35109 // activate all subpanels if not currently active..
35111 setActiveState : function(active){
35112 this.active = active;
35113 this.setActiveClass(active);
35116 this.fireEvent("deactivate", this);
35120 this.fireEvent("activate", this);
35121 // not sure if this should happen before or after..
35122 if (!this.layout) {
35123 return; // should not happen..
35126 for (var r in this.layout.regions) {
35127 reg = this.layout.getRegion(r);
35128 if (reg.getActivePanel()) {
35129 //reg.showPanel(reg.getActivePanel()); // force it to activate..
35130 reg.setActivePanel(reg.getActivePanel());
35133 if (!reg.panels.length) {
35136 reg.showPanel(reg.getPanel(0));
35145 * Returns the nested BorderLayout for this panel
35146 * @return {Roo.BorderLayout}
35148 getLayout : function(){
35149 return this.layout;
35153 * Adds a xtype elements to the layout of the nested panel
35157 xtype : 'ContentPanel',
35164 xtype : 'NestedLayoutPanel',
35170 items : [ ... list of content panels or nested layout panels.. ]
35174 * @param {Object} cfg Xtype definition of item to add.
35176 addxtype : function(cfg) {
35177 return this.layout.addxtype(cfg);
35182 * Ext JS Library 1.1.1
35183 * Copyright(c) 2006-2007, Ext JS, LLC.
35185 * Originally Released Under LGPL - original licence link has changed is not relivant.
35188 * <script type="text/javascript">
35191 * @class Roo.TabPanel
35192 * @extends Roo.util.Observable
35193 * A lightweight tab container.
35197 // basic tabs 1, built from existing content
35198 var tabs = new Roo.TabPanel("tabs1");
35199 tabs.addTab("script", "View Script");
35200 tabs.addTab("markup", "View Markup");
35201 tabs.activate("script");
35203 // more advanced tabs, built from javascript
35204 var jtabs = new Roo.TabPanel("jtabs");
35205 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
35207 // set up the UpdateManager
35208 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
35209 var updater = tab2.getUpdateManager();
35210 updater.setDefaultUrl("ajax1.htm");
35211 tab2.on('activate', updater.refresh, updater, true);
35213 // Use setUrl for Ajax loading
35214 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
35215 tab3.setUrl("ajax2.htm", null, true);
35218 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
35221 jtabs.activate("jtabs-1");
35224 * Create a new TabPanel.
35225 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
35226 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
35228 Roo.bootstrap.panel.Tabs = function(config){
35230 * The container element for this TabPanel.
35231 * @type Roo.Element
35233 this.el = Roo.get(config.el);
35236 if(typeof config == "boolean"){
35237 this.tabPosition = config ? "bottom" : "top";
35239 Roo.apply(this, config);
35243 if(this.tabPosition == "bottom"){
35244 this.bodyEl = Roo.get(this.createBody(this.el.dom));
35245 this.el.addClass("roo-tabs-bottom");
35247 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
35248 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
35249 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
35251 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
35253 if(this.tabPosition != "bottom"){
35254 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
35255 * @type Roo.Element
35257 this.bodyEl = Roo.get(this.createBody(this.el.dom));
35258 this.el.addClass("roo-tabs-top");
35262 this.bodyEl.setStyle("position", "relative");
35264 this.active = null;
35265 this.activateDelegate = this.activate.createDelegate(this);
35270 * Fires when the active tab changes
35271 * @param {Roo.TabPanel} this
35272 * @param {Roo.TabPanelItem} activePanel The new active tab
35276 * @event beforetabchange
35277 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
35278 * @param {Roo.TabPanel} this
35279 * @param {Object} e Set cancel to true on this object to cancel the tab change
35280 * @param {Roo.TabPanelItem} tab The tab being changed to
35282 "beforetabchange" : true
35285 Roo.EventManager.onWindowResize(this.onResize, this);
35286 this.cpad = this.el.getPadding("lr");
35287 this.hiddenCount = 0;
35290 // toolbar on the tabbar support...
35291 if (this.toolbar) {
35292 alert("no toolbar support yet");
35293 this.toolbar = false;
35295 var tcfg = this.toolbar;
35296 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
35297 this.toolbar = new Roo.Toolbar(tcfg);
35298 if (Roo.isSafari) {
35299 var tbl = tcfg.container.child('table', true);
35300 tbl.setAttribute('width', '100%');
35308 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
35311 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
35313 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
35315 tabPosition : "top",
35317 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
35319 currentTabWidth : 0,
35321 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
35325 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
35329 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
35331 preferredTabWidth : 175,
35333 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
35335 resizeTabs : false,
35337 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
35339 monitorResize : true,
35341 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
35346 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
35347 * @param {String} id The id of the div to use <b>or create</b>
35348 * @param {String} text The text for the tab
35349 * @param {String} content (optional) Content to put in the TabPanelItem body
35350 * @param {Boolean} closable (optional) True to create a close icon on the tab
35351 * @return {Roo.TabPanelItem} The created TabPanelItem
35353 addTab : function(id, text, content, closable, tpl)
35355 var item = new Roo.bootstrap.panel.TabItem({
35359 closable : closable,
35362 this.addTabItem(item);
35364 item.setContent(content);
35370 * Returns the {@link Roo.TabPanelItem} with the specified id/index
35371 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
35372 * @return {Roo.TabPanelItem}
35374 getTab : function(id){
35375 return this.items[id];
35379 * Hides the {@link Roo.TabPanelItem} with the specified id/index
35380 * @param {String/Number} id The id or index of the TabPanelItem to hide.
35382 hideTab : function(id){
35383 var t = this.items[id];
35386 this.hiddenCount++;
35387 this.autoSizeTabs();
35392 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
35393 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
35395 unhideTab : function(id){
35396 var t = this.items[id];
35398 t.setHidden(false);
35399 this.hiddenCount--;
35400 this.autoSizeTabs();
35405 * Adds an existing {@link Roo.TabPanelItem}.
35406 * @param {Roo.TabPanelItem} item The TabPanelItem to add
35408 addTabItem : function(item){
35409 this.items[item.id] = item;
35410 this.items.push(item);
35411 // if(this.resizeTabs){
35412 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
35413 // this.autoSizeTabs();
35415 // item.autoSize();
35420 * Removes a {@link Roo.TabPanelItem}.
35421 * @param {String/Number} id The id or index of the TabPanelItem to remove.
35423 removeTab : function(id){
35424 var items = this.items;
35425 var tab = items[id];
35426 if(!tab) { return; }
35427 var index = items.indexOf(tab);
35428 if(this.active == tab && items.length > 1){
35429 var newTab = this.getNextAvailable(index);
35434 this.stripEl.dom.removeChild(tab.pnode.dom);
35435 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
35436 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
35438 items.splice(index, 1);
35439 delete this.items[tab.id];
35440 tab.fireEvent("close", tab);
35441 tab.purgeListeners();
35442 this.autoSizeTabs();
35445 getNextAvailable : function(start){
35446 var items = this.items;
35448 // look for a next tab that will slide over to
35449 // replace the one being removed
35450 while(index < items.length){
35451 var item = items[++index];
35452 if(item && !item.isHidden()){
35456 // if one isn't found select the previous tab (on the left)
35459 var item = items[--index];
35460 if(item && !item.isHidden()){
35468 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
35469 * @param {String/Number} id The id or index of the TabPanelItem to disable.
35471 disableTab : function(id){
35472 var tab = this.items[id];
35473 if(tab && this.active != tab){
35479 * Enables a {@link Roo.TabPanelItem} that is disabled.
35480 * @param {String/Number} id The id or index of the TabPanelItem to enable.
35482 enableTab : function(id){
35483 var tab = this.items[id];
35488 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
35489 * @param {String/Number} id The id or index of the TabPanelItem to activate.
35490 * @return {Roo.TabPanelItem} The TabPanelItem.
35492 activate : function(id){
35493 var tab = this.items[id];
35497 if(tab == this.active || tab.disabled){
35501 this.fireEvent("beforetabchange", this, e, tab);
35502 if(e.cancel !== true && !tab.disabled){
35504 this.active.hide();
35506 this.active = this.items[id];
35507 this.active.show();
35508 this.fireEvent("tabchange", this, this.active);
35514 * Gets the active {@link Roo.TabPanelItem}.
35515 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
35517 getActiveTab : function(){
35518 return this.active;
35522 * Updates the tab body element to fit the height of the container element
35523 * for overflow scrolling
35524 * @param {Number} targetHeight (optional) Override the starting height from the elements height
35526 syncHeight : function(targetHeight){
35527 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
35528 var bm = this.bodyEl.getMargins();
35529 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
35530 this.bodyEl.setHeight(newHeight);
35534 onResize : function(){
35535 if(this.monitorResize){
35536 this.autoSizeTabs();
35541 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
35543 beginUpdate : function(){
35544 this.updating = true;
35548 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
35550 endUpdate : function(){
35551 this.updating = false;
35552 this.autoSizeTabs();
35556 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
35558 autoSizeTabs : function(){
35559 var count = this.items.length;
35560 var vcount = count - this.hiddenCount;
35561 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
35564 var w = Math.max(this.el.getWidth() - this.cpad, 10);
35565 var availWidth = Math.floor(w / vcount);
35566 var b = this.stripBody;
35567 if(b.getWidth() > w){
35568 var tabs = this.items;
35569 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
35570 if(availWidth < this.minTabWidth){
35571 /*if(!this.sleft){ // incomplete scrolling code
35572 this.createScrollButtons();
35575 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
35578 if(this.currentTabWidth < this.preferredTabWidth){
35579 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
35585 * Returns the number of tabs in this TabPanel.
35588 getCount : function(){
35589 return this.items.length;
35593 * Resizes all the tabs to the passed width
35594 * @param {Number} The new width
35596 setTabWidth : function(width){
35597 this.currentTabWidth = width;
35598 for(var i = 0, len = this.items.length; i < len; i++) {
35599 if(!this.items[i].isHidden()) {
35600 this.items[i].setWidth(width);
35606 * Destroys this TabPanel
35607 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
35609 destroy : function(removeEl){
35610 Roo.EventManager.removeResizeListener(this.onResize, this);
35611 for(var i = 0, len = this.items.length; i < len; i++){
35612 this.items[i].purgeListeners();
35614 if(removeEl === true){
35615 this.el.update("");
35620 createStrip : function(container)
35622 var strip = document.createElement("nav");
35623 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
35624 container.appendChild(strip);
35628 createStripList : function(strip)
35630 // div wrapper for retard IE
35631 // returns the "tr" element.
35632 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
35633 //'<div class="x-tabs-strip-wrap">'+
35634 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
35635 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
35636 return strip.firstChild; //.firstChild.firstChild.firstChild;
35638 createBody : function(container)
35640 var body = document.createElement("div");
35641 Roo.id(body, "tab-body");
35642 //Roo.fly(body).addClass("x-tabs-body");
35643 Roo.fly(body).addClass("tab-content");
35644 container.appendChild(body);
35647 createItemBody :function(bodyEl, id){
35648 var body = Roo.getDom(id);
35650 body = document.createElement("div");
35653 //Roo.fly(body).addClass("x-tabs-item-body");
35654 Roo.fly(body).addClass("tab-pane");
35655 bodyEl.insertBefore(body, bodyEl.firstChild);
35659 createStripElements : function(stripEl, text, closable, tpl)
35661 var td = document.createElement("li"); // was td..
35664 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
35667 stripEl.appendChild(td);
35669 td.className = "x-tabs-closable";
35670 if(!this.closeTpl){
35671 this.closeTpl = new Roo.Template(
35672 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
35673 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
35674 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
35677 var el = this.closeTpl.overwrite(td, {"text": text});
35678 var close = el.getElementsByTagName("div")[0];
35679 var inner = el.getElementsByTagName("em")[0];
35680 return {"el": el, "close": close, "inner": inner};
35683 // not sure what this is..
35684 // if(!this.tabTpl){
35685 //this.tabTpl = new Roo.Template(
35686 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
35687 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
35689 // this.tabTpl = new Roo.Template(
35690 // '<a href="#">' +
35691 // '<span unselectable="on"' +
35692 // (this.disableTooltips ? '' : ' title="{text}"') +
35693 // ' >{text}</span></a>'
35699 var template = tpl || this.tabTpl || false;
35703 template = new Roo.Template(
35705 '<span unselectable="on"' +
35706 (this.disableTooltips ? '' : ' title="{text}"') +
35707 ' >{text}</span></a>'
35711 switch (typeof(template)) {
35715 template = new Roo.Template(template);
35721 var el = template.overwrite(td, {"text": text});
35723 var inner = el.getElementsByTagName("span")[0];
35725 return {"el": el, "inner": inner};
35733 * @class Roo.TabPanelItem
35734 * @extends Roo.util.Observable
35735 * Represents an individual item (tab plus body) in a TabPanel.
35736 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
35737 * @param {String} id The id of this TabPanelItem
35738 * @param {String} text The text for the tab of this TabPanelItem
35739 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
35741 Roo.bootstrap.panel.TabItem = function(config){
35743 * The {@link Roo.TabPanel} this TabPanelItem belongs to
35744 * @type Roo.TabPanel
35746 this.tabPanel = config.panel;
35748 * The id for this TabPanelItem
35751 this.id = config.id;
35753 this.disabled = false;
35755 this.text = config.text;
35757 this.loaded = false;
35758 this.closable = config.closable;
35761 * The body element for this TabPanelItem.
35762 * @type Roo.Element
35764 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
35765 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
35766 this.bodyEl.setStyle("display", "block");
35767 this.bodyEl.setStyle("zoom", "1");
35768 //this.hideAction();
35770 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
35772 this.el = Roo.get(els.el);
35773 this.inner = Roo.get(els.inner, true);
35774 this.textEl = Roo.get(this.el.dom.firstChild, true);
35775 this.pnode = Roo.get(els.el.parentNode, true);
35776 this.el.on("mousedown", this.onTabMouseDown, this);
35777 this.el.on("click", this.onTabClick, this);
35779 if(config.closable){
35780 var c = Roo.get(els.close, true);
35781 c.dom.title = this.closeText;
35782 c.addClassOnOver("close-over");
35783 c.on("click", this.closeClick, this);
35789 * Fires when this tab becomes the active tab.
35790 * @param {Roo.TabPanel} tabPanel The parent TabPanel
35791 * @param {Roo.TabPanelItem} this
35795 * @event beforeclose
35796 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
35797 * @param {Roo.TabPanelItem} this
35798 * @param {Object} e Set cancel to true on this object to cancel the close.
35800 "beforeclose": true,
35803 * Fires when this tab is closed.
35804 * @param {Roo.TabPanelItem} this
35808 * @event deactivate
35809 * Fires when this tab is no longer the active tab.
35810 * @param {Roo.TabPanel} tabPanel The parent TabPanel
35811 * @param {Roo.TabPanelItem} this
35813 "deactivate" : true
35815 this.hidden = false;
35817 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
35820 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
35822 purgeListeners : function(){
35823 Roo.util.Observable.prototype.purgeListeners.call(this);
35824 this.el.removeAllListeners();
35827 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
35830 this.pnode.addClass("active");
35833 this.tabPanel.stripWrap.repaint();
35835 this.fireEvent("activate", this.tabPanel, this);
35839 * Returns true if this tab is the active tab.
35840 * @return {Boolean}
35842 isActive : function(){
35843 return this.tabPanel.getActiveTab() == this;
35847 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
35850 this.pnode.removeClass("active");
35852 this.fireEvent("deactivate", this.tabPanel, this);
35855 hideAction : function(){
35856 this.bodyEl.hide();
35857 this.bodyEl.setStyle("position", "absolute");
35858 this.bodyEl.setLeft("-20000px");
35859 this.bodyEl.setTop("-20000px");
35862 showAction : function(){
35863 this.bodyEl.setStyle("position", "relative");
35864 this.bodyEl.setTop("");
35865 this.bodyEl.setLeft("");
35866 this.bodyEl.show();
35870 * Set the tooltip for the tab.
35871 * @param {String} tooltip The tab's tooltip
35873 setTooltip : function(text){
35874 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
35875 this.textEl.dom.qtip = text;
35876 this.textEl.dom.removeAttribute('title');
35878 this.textEl.dom.title = text;
35882 onTabClick : function(e){
35883 e.preventDefault();
35884 this.tabPanel.activate(this.id);
35887 onTabMouseDown : function(e){
35888 e.preventDefault();
35889 this.tabPanel.activate(this.id);
35892 getWidth : function(){
35893 return this.inner.getWidth();
35896 setWidth : function(width){
35897 var iwidth = width - this.pnode.getPadding("lr");
35898 this.inner.setWidth(iwidth);
35899 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
35900 this.pnode.setWidth(width);
35904 * Show or hide the tab
35905 * @param {Boolean} hidden True to hide or false to show.
35907 setHidden : function(hidden){
35908 this.hidden = hidden;
35909 this.pnode.setStyle("display", hidden ? "none" : "");
35913 * Returns true if this tab is "hidden"
35914 * @return {Boolean}
35916 isHidden : function(){
35917 return this.hidden;
35921 * Returns the text for this tab
35924 getText : function(){
35928 autoSize : function(){
35929 //this.el.beginMeasure();
35930 this.textEl.setWidth(1);
35932 * #2804 [new] Tabs in Roojs
35933 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
35935 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
35936 //this.el.endMeasure();
35940 * Sets the text for the tab (Note: this also sets the tooltip text)
35941 * @param {String} text The tab's text and tooltip
35943 setText : function(text){
35945 this.textEl.update(text);
35946 this.setTooltip(text);
35947 //if(!this.tabPanel.resizeTabs){
35948 // this.autoSize();
35952 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
35954 activate : function(){
35955 this.tabPanel.activate(this.id);
35959 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
35961 disable : function(){
35962 if(this.tabPanel.active != this){
35963 this.disabled = true;
35964 this.pnode.addClass("disabled");
35969 * Enables this TabPanelItem if it was previously disabled.
35971 enable : function(){
35972 this.disabled = false;
35973 this.pnode.removeClass("disabled");
35977 * Sets the content for this TabPanelItem.
35978 * @param {String} content The content
35979 * @param {Boolean} loadScripts true to look for and load scripts
35981 setContent : function(content, loadScripts){
35982 this.bodyEl.update(content, loadScripts);
35986 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
35987 * @return {Roo.UpdateManager} The UpdateManager
35989 getUpdateManager : function(){
35990 return this.bodyEl.getUpdateManager();
35994 * Set a URL to be used to load the content for this TabPanelItem.
35995 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
35996 * @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)
35997 * @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)
35998 * @return {Roo.UpdateManager} The UpdateManager
36000 setUrl : function(url, params, loadOnce){
36001 if(this.refreshDelegate){
36002 this.un('activate', this.refreshDelegate);
36004 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
36005 this.on("activate", this.refreshDelegate);
36006 return this.bodyEl.getUpdateManager();
36010 _handleRefresh : function(url, params, loadOnce){
36011 if(!loadOnce || !this.loaded){
36012 var updater = this.bodyEl.getUpdateManager();
36013 updater.update(url, params, this._setLoaded.createDelegate(this));
36018 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
36019 * Will fail silently if the setUrl method has not been called.
36020 * This does not activate the panel, just updates its content.
36022 refresh : function(){
36023 if(this.refreshDelegate){
36024 this.loaded = false;
36025 this.refreshDelegate();
36030 _setLoaded : function(){
36031 this.loaded = true;
36035 closeClick : function(e){
36038 this.fireEvent("beforeclose", this, o);
36039 if(o.cancel !== true){
36040 this.tabPanel.removeTab(this.id);
36044 * The text displayed in the tooltip for the close icon.
36047 closeText : "Close this tab"