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]);
8852 if(this.allowBlank && !this.getRawValue().length){
8857 this.indicator.hide();
8860 this.el.addClass(this.validClass);
8862 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8864 var feedback = this.el.select('.form-control-feedback', true).first();
8867 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8868 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8873 this.fireEvent('valid', this);
8877 * Mark this field as invalid
8878 * @param {String} msg The validation message
8880 markInvalid : function(msg)
8882 if(!this.el || this.preventMark){ // not rendered
8886 this.el.removeClass([this.invalidClass, this.validClass]);
8888 var feedback = this.el.select('.form-control-feedback', true).first();
8891 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8898 if(this.allowBlank && !this.getRawValue().length){
8903 this.indicator.show();
8906 this.el.addClass(this.invalidClass);
8908 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8910 var feedback = this.el.select('.form-control-feedback', true).first();
8913 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8915 if(this.getValue().length || this.forceFeedback){
8916 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8923 this.fireEvent('invalid', this, msg);
8926 SafariOnKeyDown : function(event)
8928 // this is a workaround for a password hang bug on chrome/ webkit.
8929 if (this.inputEl().dom.type != 'password') {
8933 var isSelectAll = false;
8935 if(this.inputEl().dom.selectionEnd > 0){
8936 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8938 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8939 event.preventDefault();
8944 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
8946 event.preventDefault();
8947 // this is very hacky as keydown always get's upper case.
8949 var cc = String.fromCharCode(event.getCharCode());
8950 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
8954 adjustWidth : function(tag, w){
8955 tag = tag.toLowerCase();
8956 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8957 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8961 if(tag == 'textarea'){
8964 }else if(Roo.isOpera){
8968 if(tag == 'textarea'){
8987 * @class Roo.bootstrap.TextArea
8988 * @extends Roo.bootstrap.Input
8989 * Bootstrap TextArea class
8990 * @cfg {Number} cols Specifies the visible width of a text area
8991 * @cfg {Number} rows Specifies the visible number of lines in a text area
8992 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8993 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8994 * @cfg {string} html text
8997 * Create a new TextArea
8998 * @param {Object} config The config object
9001 Roo.bootstrap.TextArea = function(config){
9002 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9006 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
9016 getAutoCreate : function(){
9018 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9029 value : this.value || '',
9030 html: this.html || '',
9031 cls : 'form-control',
9032 placeholder : this.placeholder || ''
9036 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9037 input.maxLength = this.maxLength;
9041 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9045 input.cols = this.cols;
9048 if (this.readOnly) {
9049 input.readonly = true;
9053 input.name = this.name;
9057 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9061 ['xs','sm','md','lg'].map(function(size){
9062 if (settings[size]) {
9063 cfg.cls += ' col-' + size + '-' + settings[size];
9067 var inputblock = input;
9069 if(this.hasFeedback && !this.allowBlank){
9073 cls: 'glyphicon form-control-feedback'
9077 cls : 'has-feedback',
9086 if (this.before || this.after) {
9089 cls : 'input-group',
9093 inputblock.cn.push({
9095 cls : 'input-group-addon',
9100 inputblock.cn.push(input);
9102 if(this.hasFeedback && !this.allowBlank){
9103 inputblock.cls += ' has-feedback';
9104 inputblock.cn.push(feedback);
9108 inputblock.cn.push({
9110 cls : 'input-group-addon',
9117 if (align ==='left' && this.fieldLabel.length) {
9118 // Roo.log("left and has label");
9124 cls : 'control-label col-sm-' + this.labelWidth,
9125 html : this.fieldLabel
9129 cls : "col-sm-" + (12 - this.labelWidth),
9136 } else if ( this.fieldLabel.length) {
9137 // Roo.log(" label");
9142 //cls : 'input-group-addon',
9143 html : this.fieldLabel
9153 // Roo.log(" no label && no align");
9163 if (this.disabled) {
9164 input.disabled=true;
9171 * return the real textarea element.
9173 inputEl: function ()
9175 return this.el.select('textarea.form-control',true).first();
9179 * Clear any invalid styles/messages for this field
9181 clearInvalid : function()
9184 if(!this.el || this.preventMark){ // not rendered
9188 var label = this.el.select('label', true).first();
9189 var icon = this.el.select('i.fa-star', true).first();
9195 this.el.removeClass(this.invalidClass);
9197 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9199 var feedback = this.el.select('.form-control-feedback', true).first();
9202 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9207 this.fireEvent('valid', this);
9211 * Mark this field as valid
9213 markValid : function()
9215 if(!this.el || this.preventMark){ // not rendered
9219 this.el.removeClass([this.invalidClass, this.validClass]);
9221 var feedback = this.el.select('.form-control-feedback', true).first();
9224 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9227 if(this.disabled || this.allowBlank){
9231 var label = this.el.select('label', true).first();
9232 var icon = this.el.select('i.fa-star', true).first();
9238 this.el.addClass(this.validClass);
9240 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9242 var feedback = this.el.select('.form-control-feedback', true).first();
9245 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9246 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9251 this.fireEvent('valid', this);
9255 * Mark this field as invalid
9256 * @param {String} msg The validation message
9258 markInvalid : function(msg)
9260 if(!this.el || this.preventMark){ // not rendered
9264 this.el.removeClass([this.invalidClass, this.validClass]);
9266 var feedback = this.el.select('.form-control-feedback', true).first();
9269 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9272 if(this.disabled || this.allowBlank){
9276 var label = this.el.select('label', true).first();
9277 var icon = this.el.select('i.fa-star', true).first();
9279 if(!this.getValue().length && label && !icon){
9280 this.el.createChild({
9282 cls : 'text-danger fa fa-lg fa-star',
9283 tooltip : 'This field is required',
9284 style : 'margin-right:5px;'
9288 this.el.addClass(this.invalidClass);
9290 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9292 var feedback = this.el.select('.form-control-feedback', true).first();
9295 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9297 if(this.getValue().length || this.forceFeedback){
9298 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9305 this.fireEvent('invalid', this, msg);
9313 * trigger field - base class for combo..
9318 * @class Roo.bootstrap.TriggerField
9319 * @extends Roo.bootstrap.Input
9320 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9321 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9322 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9323 * for which you can provide a custom implementation. For example:
9325 var trigger = new Roo.bootstrap.TriggerField();
9326 trigger.onTriggerClick = myTriggerFn;
9327 trigger.applyTo('my-field');
9330 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9331 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9332 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
9333 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9334 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9337 * Create a new TriggerField.
9338 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9339 * to the base TextField)
9341 Roo.bootstrap.TriggerField = function(config){
9342 this.mimicing = false;
9343 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9346 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
9348 * @cfg {String} triggerClass A CSS class to apply to the trigger
9351 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9356 * @cfg {Boolean} removable (true|false) special filter default false
9360 /** @cfg {Boolean} grow @hide */
9361 /** @cfg {Number} growMin @hide */
9362 /** @cfg {Number} growMax @hide */
9368 autoSize: Roo.emptyFn,
9375 actionMode : 'wrap',
9380 getAutoCreate : function(){
9382 var align = this.labelAlign || this.parentLabelAlign();
9387 cls: 'form-group' //input-group
9394 type : this.inputType,
9395 cls : 'form-control',
9396 autocomplete: 'new-password',
9397 placeholder : this.placeholder || ''
9401 input.name = this.name;
9404 input.cls += ' input-' + this.size;
9407 if (this.disabled) {
9408 input.disabled=true;
9411 var inputblock = input;
9413 if(this.hasFeedback && !this.allowBlank){
9417 cls: 'glyphicon form-control-feedback'
9420 if(this.removable && !this.editable && !this.tickable){
9422 cls : 'has-feedback',
9428 cls : 'roo-combo-removable-btn close'
9435 cls : 'has-feedback',
9444 if(this.removable && !this.editable && !this.tickable){
9446 cls : 'roo-removable',
9452 cls : 'roo-combo-removable-btn close'
9459 if (this.before || this.after) {
9462 cls : 'input-group',
9466 inputblock.cn.push({
9468 cls : 'input-group-addon',
9473 inputblock.cn.push(input);
9475 if(this.hasFeedback && !this.allowBlank){
9476 inputblock.cls += ' has-feedback';
9477 inputblock.cn.push(feedback);
9481 inputblock.cn.push({
9483 cls : 'input-group-addon',
9496 cls: 'form-hidden-field'
9510 cls: 'form-hidden-field'
9514 cls: 'roo-select2-choices',
9518 cls: 'roo-select2-search-field',
9531 cls: 'roo-select2-container input-group',
9536 // cls: 'typeahead typeahead-long dropdown-menu',
9537 // style: 'display:none'
9542 if(!this.multiple && this.showToggleBtn){
9548 if (this.caret != false) {
9551 cls: 'fa fa-' + this.caret
9558 cls : 'input-group-addon btn dropdown-toggle',
9563 cls: 'combobox-clear',
9577 combobox.cls += ' roo-select2-container-multi';
9580 if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
9582 // Roo.log("left and has label");
9586 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9587 tooltip : 'This field is required'
9592 cls : 'control-label col-sm-' + this.labelWidth,
9593 html : this.fieldLabel
9597 cls : "col-sm-" + (12 - this.labelWidth),
9605 if(this.indicatorpos == 'right'){
9610 cls : 'control-label col-sm-' + this.labelWidth,
9611 html : this.fieldLabel
9616 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9617 tooltip : 'This field is required'
9620 cls : "col-sm-" + (12 - this.labelWidth),
9629 } else if ( this.fieldLabel.length) {
9630 // Roo.log(" label");
9634 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9635 tooltip : 'This field is required'
9639 //cls : 'input-group-addon',
9640 html : this.fieldLabel
9648 if(this.indicatorpos == 'right'){
9653 //cls : 'input-group-addon',
9654 html : this.fieldLabel
9659 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9660 tooltip : 'This field is required'
9671 // Roo.log(" no label && no align");
9678 ['xs','sm','md','lg'].map(function(size){
9679 if (settings[size]) {
9680 cfg.cls += ' col-' + size + '-' + settings[size];
9691 onResize : function(w, h){
9692 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9693 // if(typeof w == 'number'){
9694 // var x = w - this.trigger.getWidth();
9695 // this.inputEl().setWidth(this.adjustWidth('input', x));
9696 // this.trigger.setStyle('left', x+'px');
9701 adjustSize : Roo.BoxComponent.prototype.adjustSize,
9704 getResizeEl : function(){
9705 return this.inputEl();
9709 getPositionEl : function(){
9710 return this.inputEl();
9714 alignErrorIcon : function(){
9715 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9719 initEvents : function(){
9723 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9724 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9725 if(!this.multiple && this.showToggleBtn){
9726 this.trigger = this.el.select('span.dropdown-toggle',true).first();
9727 if(this.hideTrigger){
9728 this.trigger.setDisplayed(false);
9730 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9734 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9737 if(this.removable && !this.editable && !this.tickable){
9738 var close = this.closeTriggerEl();
9741 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9742 close.on('click', this.removeBtnClick, this, close);
9746 //this.trigger.addClassOnOver('x-form-trigger-over');
9747 //this.trigger.addClassOnClick('x-form-trigger-click');
9750 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9754 closeTriggerEl : function()
9756 var close = this.el.select('.roo-combo-removable-btn', true).first();
9757 return close ? close : false;
9760 removeBtnClick : function(e, h, el)
9764 if(this.fireEvent("remove", this) !== false){
9766 this.fireEvent("afterremove", this)
9770 createList : function()
9772 this.list = Roo.get(document.body).createChild({
9774 cls: 'typeahead typeahead-long dropdown-menu',
9775 style: 'display:none'
9778 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9783 initTrigger : function(){
9788 onDestroy : function(){
9790 this.trigger.removeAllListeners();
9791 // this.trigger.remove();
9794 // this.wrap.remove();
9796 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9800 onFocus : function(){
9801 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9804 this.wrap.addClass('x-trigger-wrap-focus');
9805 this.mimicing = true;
9806 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9807 if(this.monitorTab){
9808 this.el.on("keydown", this.checkTab, this);
9815 checkTab : function(e){
9816 if(e.getKey() == e.TAB){
9822 onBlur : function(){
9827 mimicBlur : function(e, t){
9829 if(!this.wrap.contains(t) && this.validateBlur()){
9836 triggerBlur : function(){
9837 this.mimicing = false;
9838 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9839 if(this.monitorTab){
9840 this.el.un("keydown", this.checkTab, this);
9842 //this.wrap.removeClass('x-trigger-wrap-focus');
9843 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9847 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9848 validateBlur : function(e, t){
9853 onDisable : function(){
9854 this.inputEl().dom.disabled = true;
9855 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9857 // this.wrap.addClass('x-item-disabled');
9862 onEnable : function(){
9863 this.inputEl().dom.disabled = false;
9864 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9866 // this.el.removeClass('x-item-disabled');
9871 onShow : function(){
9872 var ae = this.getActionEl();
9875 ae.dom.style.display = '';
9876 ae.dom.style.visibility = 'visible';
9882 onHide : function(){
9883 var ae = this.getActionEl();
9884 ae.dom.style.display = 'none';
9888 * The function that should handle the trigger's click event. This method does nothing by default until overridden
9889 * by an implementing function.
9891 * @param {EventObject} e
9893 onTriggerClick : Roo.emptyFn
9897 * Ext JS Library 1.1.1
9898 * Copyright(c) 2006-2007, Ext JS, LLC.
9900 * Originally Released Under LGPL - original licence link has changed is not relivant.
9903 * <script type="text/javascript">
9908 * @class Roo.data.SortTypes
9910 * Defines the default sorting (casting?) comparison functions used when sorting data.
9912 Roo.data.SortTypes = {
9914 * Default sort that does nothing
9915 * @param {Mixed} s The value being converted
9916 * @return {Mixed} The comparison value
9923 * The regular expression used to strip tags
9927 stripTagsRE : /<\/?[^>]+>/gi,
9930 * Strips all HTML tags to sort on text only
9931 * @param {Mixed} s The value being converted
9932 * @return {String} The comparison value
9934 asText : function(s){
9935 return String(s).replace(this.stripTagsRE, "");
9939 * Strips all HTML tags to sort on text only - Case insensitive
9940 * @param {Mixed} s The value being converted
9941 * @return {String} The comparison value
9943 asUCText : function(s){
9944 return String(s).toUpperCase().replace(this.stripTagsRE, "");
9948 * Case insensitive string
9949 * @param {Mixed} s The value being converted
9950 * @return {String} The comparison value
9952 asUCString : function(s) {
9953 return String(s).toUpperCase();
9958 * @param {Mixed} s The value being converted
9959 * @return {Number} The comparison value
9961 asDate : function(s) {
9965 if(s instanceof Date){
9968 return Date.parse(String(s));
9973 * @param {Mixed} s The value being converted
9974 * @return {Float} The comparison value
9976 asFloat : function(s) {
9977 var val = parseFloat(String(s).replace(/,/g, ""));
9986 * @param {Mixed} s The value being converted
9987 * @return {Number} The comparison value
9989 asInt : function(s) {
9990 var val = parseInt(String(s).replace(/,/g, ""));
9998 * Ext JS Library 1.1.1
9999 * Copyright(c) 2006-2007, Ext JS, LLC.
10001 * Originally Released Under LGPL - original licence link has changed is not relivant.
10004 * <script type="text/javascript">
10008 * @class Roo.data.Record
10009 * Instances of this class encapsulate both record <em>definition</em> information, and record
10010 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10011 * to access Records cached in an {@link Roo.data.Store} object.<br>
10013 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10014 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10017 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10019 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10020 * {@link #create}. The parameters are the same.
10021 * @param {Array} data An associative Array of data values keyed by the field name.
10022 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10023 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10024 * not specified an integer id is generated.
10026 Roo.data.Record = function(data, id){
10027 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10032 * Generate a constructor for a specific record layout.
10033 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10034 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10035 * Each field definition object may contain the following properties: <ul>
10036 * <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,
10037 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10038 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10039 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10040 * is being used, then this is a string containing the javascript expression to reference the data relative to
10041 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10042 * to the data item relative to the record element. If the mapping expression is the same as the field name,
10043 * this may be omitted.</p></li>
10044 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10045 * <ul><li>auto (Default, implies no conversion)</li>
10050 * <li>date</li></ul></p></li>
10051 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10052 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10053 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10054 * by the Reader into an object that will be stored in the Record. It is passed the
10055 * following parameters:<ul>
10056 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10058 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10060 * <br>usage:<br><pre><code>
10061 var TopicRecord = Roo.data.Record.create(
10062 {name: 'title', mapping: 'topic_title'},
10063 {name: 'author', mapping: 'username'},
10064 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10065 {name: 'lastPost', mapping: 'post_time', type: 'date'},
10066 {name: 'lastPoster', mapping: 'user2'},
10067 {name: 'excerpt', mapping: 'post_text'}
10070 var myNewRecord = new TopicRecord({
10071 title: 'Do my job please',
10074 lastPost: new Date(),
10075 lastPoster: 'Animal',
10076 excerpt: 'No way dude!'
10078 myStore.add(myNewRecord);
10083 Roo.data.Record.create = function(o){
10084 var f = function(){
10085 f.superclass.constructor.apply(this, arguments);
10087 Roo.extend(f, Roo.data.Record);
10088 var p = f.prototype;
10089 p.fields = new Roo.util.MixedCollection(false, function(field){
10092 for(var i = 0, len = o.length; i < len; i++){
10093 p.fields.add(new Roo.data.Field(o[i]));
10095 f.getField = function(name){
10096 return p.fields.get(name);
10101 Roo.data.Record.AUTO_ID = 1000;
10102 Roo.data.Record.EDIT = 'edit';
10103 Roo.data.Record.REJECT = 'reject';
10104 Roo.data.Record.COMMIT = 'commit';
10106 Roo.data.Record.prototype = {
10108 * Readonly flag - true if this record has been modified.
10117 join : function(store){
10118 this.store = store;
10122 * Set the named field to the specified value.
10123 * @param {String} name The name of the field to set.
10124 * @param {Object} value The value to set the field to.
10126 set : function(name, value){
10127 if(this.data[name] == value){
10131 if(!this.modified){
10132 this.modified = {};
10134 if(typeof this.modified[name] == 'undefined'){
10135 this.modified[name] = this.data[name];
10137 this.data[name] = value;
10138 if(!this.editing && this.store){
10139 this.store.afterEdit(this);
10144 * Get the value of the named field.
10145 * @param {String} name The name of the field to get the value of.
10146 * @return {Object} The value of the field.
10148 get : function(name){
10149 return this.data[name];
10153 beginEdit : function(){
10154 this.editing = true;
10155 this.modified = {};
10159 cancelEdit : function(){
10160 this.editing = false;
10161 delete this.modified;
10165 endEdit : function(){
10166 this.editing = false;
10167 if(this.dirty && this.store){
10168 this.store.afterEdit(this);
10173 * Usually called by the {@link Roo.data.Store} which owns the Record.
10174 * Rejects all changes made to the Record since either creation, or the last commit operation.
10175 * Modified fields are reverted to their original values.
10177 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10178 * of reject operations.
10180 reject : function(){
10181 var m = this.modified;
10183 if(typeof m[n] != "function"){
10184 this.data[n] = m[n];
10187 this.dirty = false;
10188 delete this.modified;
10189 this.editing = false;
10191 this.store.afterReject(this);
10196 * Usually called by the {@link Roo.data.Store} which owns the Record.
10197 * Commits all changes made to the Record since either creation, or the last commit operation.
10199 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10200 * of commit operations.
10202 commit : function(){
10203 this.dirty = false;
10204 delete this.modified;
10205 this.editing = false;
10207 this.store.afterCommit(this);
10212 hasError : function(){
10213 return this.error != null;
10217 clearError : function(){
10222 * Creates a copy of this record.
10223 * @param {String} id (optional) A new record id if you don't want to use this record's id
10226 copy : function(newId) {
10227 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10231 * Ext JS Library 1.1.1
10232 * Copyright(c) 2006-2007, Ext JS, LLC.
10234 * Originally Released Under LGPL - original licence link has changed is not relivant.
10237 * <script type="text/javascript">
10243 * @class Roo.data.Store
10244 * @extends Roo.util.Observable
10245 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10246 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10248 * 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
10249 * has no knowledge of the format of the data returned by the Proxy.<br>
10251 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10252 * instances from the data object. These records are cached and made available through accessor functions.
10254 * Creates a new Store.
10255 * @param {Object} config A config object containing the objects needed for the Store to access data,
10256 * and read the data into Records.
10258 Roo.data.Store = function(config){
10259 this.data = new Roo.util.MixedCollection(false);
10260 this.data.getKey = function(o){
10263 this.baseParams = {};
10265 this.paramNames = {
10270 "multisort" : "_multisort"
10273 if(config && config.data){
10274 this.inlineData = config.data;
10275 delete config.data;
10278 Roo.apply(this, config);
10280 if(this.reader){ // reader passed
10281 this.reader = Roo.factory(this.reader, Roo.data);
10282 this.reader.xmodule = this.xmodule || false;
10283 if(!this.recordType){
10284 this.recordType = this.reader.recordType;
10286 if(this.reader.onMetaChange){
10287 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10291 if(this.recordType){
10292 this.fields = this.recordType.prototype.fields;
10294 this.modified = [];
10298 * @event datachanged
10299 * Fires when the data cache has changed, and a widget which is using this Store
10300 * as a Record cache should refresh its view.
10301 * @param {Store} this
10303 datachanged : true,
10305 * @event metachange
10306 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10307 * @param {Store} this
10308 * @param {Object} meta The JSON metadata
10313 * Fires when Records have been added to the Store
10314 * @param {Store} this
10315 * @param {Roo.data.Record[]} records The array of Records added
10316 * @param {Number} index The index at which the record(s) were added
10321 * Fires when a Record has been removed from the Store
10322 * @param {Store} this
10323 * @param {Roo.data.Record} record The Record that was removed
10324 * @param {Number} index The index at which the record was removed
10329 * Fires when a Record has been updated
10330 * @param {Store} this
10331 * @param {Roo.data.Record} record The Record that was updated
10332 * @param {String} operation The update operation being performed. Value may be one of:
10334 Roo.data.Record.EDIT
10335 Roo.data.Record.REJECT
10336 Roo.data.Record.COMMIT
10342 * Fires when the data cache has been cleared.
10343 * @param {Store} this
10347 * @event beforeload
10348 * Fires before a request is made for a new data object. If the beforeload handler returns false
10349 * the load action will be canceled.
10350 * @param {Store} this
10351 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10355 * @event beforeloadadd
10356 * Fires after a new set of Records has been loaded.
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)
10361 beforeloadadd : true,
10364 * Fires after a new set of Records has been loaded, before they are added to the store.
10365 * @param {Store} this
10366 * @param {Roo.data.Record[]} records The Records that were loaded
10367 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10368 * @params {Object} return from reader
10372 * @event loadexception
10373 * Fires if an exception occurs in the Proxy during loading.
10374 * Called with the signature of the Proxy's "loadexception" event.
10375 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10378 * @param {Object} return from JsonData.reader() - success, totalRecords, records
10379 * @param {Object} load options
10380 * @param {Object} jsonData from your request (normally this contains the Exception)
10382 loadexception : true
10386 this.proxy = Roo.factory(this.proxy, Roo.data);
10387 this.proxy.xmodule = this.xmodule || false;
10388 this.relayEvents(this.proxy, ["loadexception"]);
10390 this.sortToggle = {};
10391 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10393 Roo.data.Store.superclass.constructor.call(this);
10395 if(this.inlineData){
10396 this.loadData(this.inlineData);
10397 delete this.inlineData;
10401 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10403 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
10404 * without a remote query - used by combo/forms at present.
10408 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10411 * @cfg {Array} data Inline data to be loaded when the store is initialized.
10414 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10415 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10418 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10419 * on any HTTP request
10422 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10425 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10429 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10430 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10432 remoteSort : false,
10435 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10436 * loaded or when a record is removed. (defaults to false).
10438 pruneModifiedRecords : false,
10441 lastOptions : null,
10444 * Add Records to the Store and fires the add event.
10445 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10447 add : function(records){
10448 records = [].concat(records);
10449 for(var i = 0, len = records.length; i < len; i++){
10450 records[i].join(this);
10452 var index = this.data.length;
10453 this.data.addAll(records);
10454 this.fireEvent("add", this, records, index);
10458 * Remove a Record from the Store and fires the remove event.
10459 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10461 remove : function(record){
10462 var index = this.data.indexOf(record);
10463 this.data.removeAt(index);
10464 if(this.pruneModifiedRecords){
10465 this.modified.remove(record);
10467 this.fireEvent("remove", this, record, index);
10471 * Remove all Records from the Store and fires the clear event.
10473 removeAll : function(){
10475 if(this.pruneModifiedRecords){
10476 this.modified = [];
10478 this.fireEvent("clear", this);
10482 * Inserts Records to the Store at the given index and fires the add event.
10483 * @param {Number} index The start index at which to insert the passed Records.
10484 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10486 insert : function(index, records){
10487 records = [].concat(records);
10488 for(var i = 0, len = records.length; i < len; i++){
10489 this.data.insert(index, records[i]);
10490 records[i].join(this);
10492 this.fireEvent("add", this, records, index);
10496 * Get the index within the cache of the passed Record.
10497 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10498 * @return {Number} The index of the passed Record. Returns -1 if not found.
10500 indexOf : function(record){
10501 return this.data.indexOf(record);
10505 * Get the index within the cache of the Record with the passed id.
10506 * @param {String} id The id of the Record to find.
10507 * @return {Number} The index of the Record. Returns -1 if not found.
10509 indexOfId : function(id){
10510 return this.data.indexOfKey(id);
10514 * Get the Record with the specified id.
10515 * @param {String} id The id of the Record to find.
10516 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10518 getById : function(id){
10519 return this.data.key(id);
10523 * Get the Record at the specified index.
10524 * @param {Number} index The index of the Record to find.
10525 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10527 getAt : function(index){
10528 return this.data.itemAt(index);
10532 * Returns a range of Records between specified indices.
10533 * @param {Number} startIndex (optional) The starting index (defaults to 0)
10534 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10535 * @return {Roo.data.Record[]} An array of Records
10537 getRange : function(start, end){
10538 return this.data.getRange(start, end);
10542 storeOptions : function(o){
10543 o = Roo.apply({}, o);
10546 this.lastOptions = o;
10550 * Loads the Record cache from the configured Proxy using the configured Reader.
10552 * If using remote paging, then the first load call must specify the <em>start</em>
10553 * and <em>limit</em> properties in the options.params property to establish the initial
10554 * position within the dataset, and the number of Records to cache on each read from the Proxy.
10556 * <strong>It is important to note that for remote data sources, loading is asynchronous,
10557 * and this call will return before the new data has been loaded. Perform any post-processing
10558 * in a callback function, or in a "load" event handler.</strong>
10560 * @param {Object} options An object containing properties which control loading options:<ul>
10561 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10562 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10563 * passed the following arguments:<ul>
10564 * <li>r : Roo.data.Record[]</li>
10565 * <li>options: Options object from the load call</li>
10566 * <li>success: Boolean success indicator</li></ul></li>
10567 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10568 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10571 load : function(options){
10572 options = options || {};
10573 if(this.fireEvent("beforeload", this, options) !== false){
10574 this.storeOptions(options);
10575 var p = Roo.apply(options.params || {}, this.baseParams);
10576 // if meta was not loaded from remote source.. try requesting it.
10577 if (!this.reader.metaFromRemote) {
10578 p._requestMeta = 1;
10580 if(this.sortInfo && this.remoteSort){
10581 var pn = this.paramNames;
10582 p[pn["sort"]] = this.sortInfo.field;
10583 p[pn["dir"]] = this.sortInfo.direction;
10585 if (this.multiSort) {
10586 var pn = this.paramNames;
10587 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10590 this.proxy.load(p, this.reader, this.loadRecords, this, options);
10595 * Reloads the Record cache from the configured Proxy using the configured Reader and
10596 * the options from the last load operation performed.
10597 * @param {Object} options (optional) An object containing properties which may override the options
10598 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10599 * the most recently used options are reused).
10601 reload : function(options){
10602 this.load(Roo.applyIf(options||{}, this.lastOptions));
10606 // Called as a callback by the Reader during a load operation.
10607 loadRecords : function(o, options, success){
10608 if(!o || success === false){
10609 if(success !== false){
10610 this.fireEvent("load", this, [], options, o);
10612 if(options.callback){
10613 options.callback.call(options.scope || this, [], options, false);
10617 // if data returned failure - throw an exception.
10618 if (o.success === false) {
10619 // show a message if no listener is registered.
10620 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10621 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10623 // loadmask wil be hooked into this..
10624 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10627 var r = o.records, t = o.totalRecords || r.length;
10629 this.fireEvent("beforeloadadd", this, r, options, o);
10631 if(!options || options.add !== true){
10632 if(this.pruneModifiedRecords){
10633 this.modified = [];
10635 for(var i = 0, len = r.length; i < len; i++){
10639 this.data = this.snapshot;
10640 delete this.snapshot;
10643 this.data.addAll(r);
10644 this.totalLength = t;
10646 this.fireEvent("datachanged", this);
10648 this.totalLength = Math.max(t, this.data.length+r.length);
10651 this.fireEvent("load", this, r, options, o);
10652 if(options.callback){
10653 options.callback.call(options.scope || this, r, options, true);
10659 * Loads data from a passed data block. A Reader which understands the format of the data
10660 * must have been configured in the constructor.
10661 * @param {Object} data The data block from which to read the Records. The format of the data expected
10662 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10663 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10665 loadData : function(o, append){
10666 var r = this.reader.readRecords(o);
10667 this.loadRecords(r, {add: append}, true);
10671 * Gets the number of cached records.
10673 * <em>If using paging, this may not be the total size of the dataset. If the data object
10674 * used by the Reader contains the dataset size, then the getTotalCount() function returns
10675 * the data set size</em>
10677 getCount : function(){
10678 return this.data.length || 0;
10682 * Gets the total number of records in the dataset as returned by the server.
10684 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10685 * the dataset size</em>
10687 getTotalCount : function(){
10688 return this.totalLength || 0;
10692 * Returns the sort state of the Store as an object with two properties:
10694 field {String} The name of the field by which the Records are sorted
10695 direction {String} The sort order, "ASC" or "DESC"
10698 getSortState : function(){
10699 return this.sortInfo;
10703 applySort : function(){
10704 if(this.sortInfo && !this.remoteSort){
10705 var s = this.sortInfo, f = s.field;
10706 var st = this.fields.get(f).sortType;
10707 var fn = function(r1, r2){
10708 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10709 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10711 this.data.sort(s.direction, fn);
10712 if(this.snapshot && this.snapshot != this.data){
10713 this.snapshot.sort(s.direction, fn);
10719 * Sets the default sort column and order to be used by the next load operation.
10720 * @param {String} fieldName The name of the field to sort by.
10721 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10723 setDefaultSort : function(field, dir){
10724 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10728 * Sort the Records.
10729 * If remote sorting is used, the sort is performed on the server, and the cache is
10730 * reloaded. If local sorting is used, the cache is sorted internally.
10731 * @param {String} fieldName The name of the field to sort by.
10732 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10734 sort : function(fieldName, dir){
10735 var f = this.fields.get(fieldName);
10737 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10739 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10740 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10745 this.sortToggle[f.name] = dir;
10746 this.sortInfo = {field: f.name, direction: dir};
10747 if(!this.remoteSort){
10749 this.fireEvent("datachanged", this);
10751 this.load(this.lastOptions);
10756 * Calls the specified function for each of the Records in the cache.
10757 * @param {Function} fn The function to call. The Record is passed as the first parameter.
10758 * Returning <em>false</em> aborts and exits the iteration.
10759 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10761 each : function(fn, scope){
10762 this.data.each(fn, scope);
10766 * Gets all records modified since the last commit. Modified records are persisted across load operations
10767 * (e.g., during paging).
10768 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10770 getModifiedRecords : function(){
10771 return this.modified;
10775 createFilterFn : function(property, value, anyMatch){
10776 if(!value.exec){ // not a regex
10777 value = String(value);
10778 if(value.length == 0){
10781 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10783 return function(r){
10784 return value.test(r.data[property]);
10789 * Sums the value of <i>property</i> for each record between start and end and returns the result.
10790 * @param {String} property A field on your records
10791 * @param {Number} start The record index to start at (defaults to 0)
10792 * @param {Number} end The last record index to include (defaults to length - 1)
10793 * @return {Number} The sum
10795 sum : function(property, start, end){
10796 var rs = this.data.items, v = 0;
10797 start = start || 0;
10798 end = (end || end === 0) ? end : rs.length-1;
10800 for(var i = start; i <= end; i++){
10801 v += (rs[i].data[property] || 0);
10807 * Filter the records by a specified property.
10808 * @param {String} field A field on your records
10809 * @param {String/RegExp} value Either a string that the field
10810 * should start with or a RegExp to test against the field
10811 * @param {Boolean} anyMatch True to match any part not just the beginning
10813 filter : function(property, value, anyMatch){
10814 var fn = this.createFilterFn(property, value, anyMatch);
10815 return fn ? this.filterBy(fn) : this.clearFilter();
10819 * Filter by a function. The specified function will be called with each
10820 * record in this data source. If the function returns true the record is included,
10821 * otherwise it is filtered.
10822 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10823 * @param {Object} scope (optional) The scope of the function (defaults to this)
10825 filterBy : function(fn, scope){
10826 this.snapshot = this.snapshot || this.data;
10827 this.data = this.queryBy(fn, scope||this);
10828 this.fireEvent("datachanged", this);
10832 * Query the records by a specified property.
10833 * @param {String} field A field on your records
10834 * @param {String/RegExp} value Either a string that the field
10835 * should start with or a RegExp to test against the field
10836 * @param {Boolean} anyMatch True to match any part not just the beginning
10837 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10839 query : function(property, value, anyMatch){
10840 var fn = this.createFilterFn(property, value, anyMatch);
10841 return fn ? this.queryBy(fn) : this.data.clone();
10845 * Query by a function. The specified function will be called with each
10846 * record in this data source. If the function returns true the record is included
10848 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10849 * @param {Object} scope (optional) The scope of the function (defaults to this)
10850 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10852 queryBy : function(fn, scope){
10853 var data = this.snapshot || this.data;
10854 return data.filterBy(fn, scope||this);
10858 * Collects unique values for a particular dataIndex from this store.
10859 * @param {String} dataIndex The property to collect
10860 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10861 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10862 * @return {Array} An array of the unique values
10864 collect : function(dataIndex, allowNull, bypassFilter){
10865 var d = (bypassFilter === true && this.snapshot) ?
10866 this.snapshot.items : this.data.items;
10867 var v, sv, r = [], l = {};
10868 for(var i = 0, len = d.length; i < len; i++){
10869 v = d[i].data[dataIndex];
10871 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10880 * Revert to a view of the Record cache with no filtering applied.
10881 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10883 clearFilter : function(suppressEvent){
10884 if(this.snapshot && this.snapshot != this.data){
10885 this.data = this.snapshot;
10886 delete this.snapshot;
10887 if(suppressEvent !== true){
10888 this.fireEvent("datachanged", this);
10894 afterEdit : function(record){
10895 if(this.modified.indexOf(record) == -1){
10896 this.modified.push(record);
10898 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10902 afterReject : function(record){
10903 this.modified.remove(record);
10904 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10908 afterCommit : function(record){
10909 this.modified.remove(record);
10910 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10914 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10915 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10917 commitChanges : function(){
10918 var m = this.modified.slice(0);
10919 this.modified = [];
10920 for(var i = 0, len = m.length; i < len; i++){
10926 * Cancel outstanding changes on all changed records.
10928 rejectChanges : function(){
10929 var m = this.modified.slice(0);
10930 this.modified = [];
10931 for(var i = 0, len = m.length; i < len; i++){
10936 onMetaChange : function(meta, rtype, o){
10937 this.recordType = rtype;
10938 this.fields = rtype.prototype.fields;
10939 delete this.snapshot;
10940 this.sortInfo = meta.sortInfo || this.sortInfo;
10941 this.modified = [];
10942 this.fireEvent('metachange', this, this.reader.meta);
10945 moveIndex : function(data, type)
10947 var index = this.indexOf(data);
10949 var newIndex = index + type;
10953 this.insert(newIndex, data);
10958 * Ext JS Library 1.1.1
10959 * Copyright(c) 2006-2007, Ext JS, LLC.
10961 * Originally Released Under LGPL - original licence link has changed is not relivant.
10964 * <script type="text/javascript">
10968 * @class Roo.data.SimpleStore
10969 * @extends Roo.data.Store
10970 * Small helper class to make creating Stores from Array data easier.
10971 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10972 * @cfg {Array} fields An array of field definition objects, or field name strings.
10973 * @cfg {Array} data The multi-dimensional array of data
10975 * @param {Object} config
10977 Roo.data.SimpleStore = function(config){
10978 Roo.data.SimpleStore.superclass.constructor.call(this, {
10980 reader: new Roo.data.ArrayReader({
10983 Roo.data.Record.create(config.fields)
10985 proxy : new Roo.data.MemoryProxy(config.data)
10989 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10991 * Ext JS Library 1.1.1
10992 * Copyright(c) 2006-2007, Ext JS, LLC.
10994 * Originally Released Under LGPL - original licence link has changed is not relivant.
10997 * <script type="text/javascript">
11002 * @extends Roo.data.Store
11003 * @class Roo.data.JsonStore
11004 * Small helper class to make creating Stores for JSON data easier. <br/>
11006 var store = new Roo.data.JsonStore({
11007 url: 'get-images.php',
11009 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11012 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11013 * JsonReader and HttpProxy (unless inline data is provided).</b>
11014 * @cfg {Array} fields An array of field definition objects, or field name strings.
11016 * @param {Object} config
11018 Roo.data.JsonStore = function(c){
11019 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11020 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11021 reader: new Roo.data.JsonReader(c, c.fields)
11024 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11026 * Ext JS Library 1.1.1
11027 * Copyright(c) 2006-2007, Ext JS, LLC.
11029 * Originally Released Under LGPL - original licence link has changed is not relivant.
11032 * <script type="text/javascript">
11036 Roo.data.Field = function(config){
11037 if(typeof config == "string"){
11038 config = {name: config};
11040 Roo.apply(this, config);
11043 this.type = "auto";
11046 var st = Roo.data.SortTypes;
11047 // named sortTypes are supported, here we look them up
11048 if(typeof this.sortType == "string"){
11049 this.sortType = st[this.sortType];
11052 // set default sortType for strings and dates
11053 if(!this.sortType){
11056 this.sortType = st.asUCString;
11059 this.sortType = st.asDate;
11062 this.sortType = st.none;
11067 var stripRe = /[\$,%]/g;
11069 // prebuilt conversion function for this field, instead of
11070 // switching every time we're reading a value
11072 var cv, dateFormat = this.dateFormat;
11077 cv = function(v){ return v; };
11080 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11084 return v !== undefined && v !== null && v !== '' ?
11085 parseInt(String(v).replace(stripRe, ""), 10) : '';
11090 return v !== undefined && v !== null && v !== '' ?
11091 parseFloat(String(v).replace(stripRe, ""), 10) : '';
11096 cv = function(v){ return v === true || v === "true" || v == 1; };
11103 if(v instanceof Date){
11107 if(dateFormat == "timestamp"){
11108 return new Date(v*1000);
11110 return Date.parseDate(v, dateFormat);
11112 var parsed = Date.parse(v);
11113 return parsed ? new Date(parsed) : null;
11122 Roo.data.Field.prototype = {
11130 * Ext JS Library 1.1.1
11131 * Copyright(c) 2006-2007, Ext JS, LLC.
11133 * Originally Released Under LGPL - original licence link has changed is not relivant.
11136 * <script type="text/javascript">
11139 // Base class for reading structured data from a data source. This class is intended to be
11140 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11143 * @class Roo.data.DataReader
11144 * Base class for reading structured data from a data source. This class is intended to be
11145 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11148 Roo.data.DataReader = function(meta, recordType){
11152 this.recordType = recordType instanceof Array ?
11153 Roo.data.Record.create(recordType) : recordType;
11156 Roo.data.DataReader.prototype = {
11158 * Create an empty record
11159 * @param {Object} data (optional) - overlay some values
11160 * @return {Roo.data.Record} record created.
11162 newRow : function(d) {
11164 this.recordType.prototype.fields.each(function(c) {
11166 case 'int' : da[c.name] = 0; break;
11167 case 'date' : da[c.name] = new Date(); break;
11168 case 'float' : da[c.name] = 0.0; break;
11169 case 'boolean' : da[c.name] = false; break;
11170 default : da[c.name] = ""; break;
11174 return new this.recordType(Roo.apply(da, d));
11179 * Ext JS Library 1.1.1
11180 * Copyright(c) 2006-2007, Ext JS, LLC.
11182 * Originally Released Under LGPL - original licence link has changed is not relivant.
11185 * <script type="text/javascript">
11189 * @class Roo.data.DataProxy
11190 * @extends Roo.data.Observable
11191 * This class is an abstract base class for implementations which provide retrieval of
11192 * unformatted data objects.<br>
11194 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11195 * (of the appropriate type which knows how to parse the data object) to provide a block of
11196 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11198 * Custom implementations must implement the load method as described in
11199 * {@link Roo.data.HttpProxy#load}.
11201 Roo.data.DataProxy = function(){
11204 * @event beforeload
11205 * Fires before a network request is made to retrieve a data object.
11206 * @param {Object} This DataProxy object.
11207 * @param {Object} params The params parameter to the load function.
11212 * Fires before the load method's callback is called.
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.
11219 * @event loadexception
11220 * Fires if an Exception occurs during data retrieval.
11221 * @param {Object} This DataProxy object.
11222 * @param {Object} o The data object.
11223 * @param {Object} arg The callback argument object passed to the load function.
11224 * @param {Object} e The Exception.
11226 loadexception : true
11228 Roo.data.DataProxy.superclass.constructor.call(this);
11231 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11234 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11238 * Ext JS Library 1.1.1
11239 * Copyright(c) 2006-2007, Ext JS, LLC.
11241 * Originally Released Under LGPL - original licence link has changed is not relivant.
11244 * <script type="text/javascript">
11247 * @class Roo.data.MemoryProxy
11248 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11249 * to the Reader when its load method is called.
11251 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11253 Roo.data.MemoryProxy = function(data){
11257 Roo.data.MemoryProxy.superclass.constructor.call(this);
11261 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11264 * Load data from the requested source (in this case an in-memory
11265 * data object passed to the constructor), read the data object into
11266 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11267 * process that block using the passed callback.
11268 * @param {Object} params This parameter is not used by the MemoryProxy class.
11269 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11270 * object into a block of Roo.data.Records.
11271 * @param {Function} callback The function into which to pass the block of Roo.data.records.
11272 * The function must be passed <ul>
11273 * <li>The Record block object</li>
11274 * <li>The "arg" argument from the load function</li>
11275 * <li>A boolean success indicator</li>
11277 * @param {Object} scope The scope in which to call the callback
11278 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11280 load : function(params, reader, callback, scope, arg){
11281 params = params || {};
11284 result = reader.readRecords(this.data);
11286 this.fireEvent("loadexception", this, arg, null, e);
11287 callback.call(scope, null, arg, false);
11290 callback.call(scope, result, arg, true);
11294 update : function(params, records){
11299 * Ext JS Library 1.1.1
11300 * Copyright(c) 2006-2007, Ext JS, LLC.
11302 * Originally Released Under LGPL - original licence link has changed is not relivant.
11305 * <script type="text/javascript">
11308 * @class Roo.data.HttpProxy
11309 * @extends Roo.data.DataProxy
11310 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11311 * configured to reference a certain URL.<br><br>
11313 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11314 * from which the running page was served.<br><br>
11316 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11318 * Be aware that to enable the browser to parse an XML document, the server must set
11319 * the Content-Type header in the HTTP response to "text/xml".
11321 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11322 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
11323 * will be used to make the request.
11325 Roo.data.HttpProxy = function(conn){
11326 Roo.data.HttpProxy.superclass.constructor.call(this);
11327 // is conn a conn config or a real conn?
11329 this.useAjax = !conn || !conn.events;
11333 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11334 // thse are take from connection...
11337 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11340 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11341 * extra parameters to each request made by this object. (defaults to undefined)
11344 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11345 * to each request made by this object. (defaults to undefined)
11348 * @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)
11351 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11354 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11360 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11364 * Return the {@link Roo.data.Connection} object being used by this Proxy.
11365 * @return {Connection} The Connection object. This object may be used to subscribe to events on
11366 * a finer-grained basis than the DataProxy events.
11368 getConnection : function(){
11369 return this.useAjax ? Roo.Ajax : this.conn;
11373 * Load data from the configured {@link Roo.data.Connection}, read the data object into
11374 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11375 * process that block using the passed callback.
11376 * @param {Object} params An object containing properties which are to be used as HTTP parameters
11377 * for the request to the remote server.
11378 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11379 * object into a block of Roo.data.Records.
11380 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11381 * The function must be passed <ul>
11382 * <li>The Record block object</li>
11383 * <li>The "arg" argument from the load function</li>
11384 * <li>A boolean success indicator</li>
11386 * @param {Object} scope The scope in which to call the callback
11387 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11389 load : function(params, reader, callback, scope, arg){
11390 if(this.fireEvent("beforeload", this, params) !== false){
11392 params : params || {},
11394 callback : callback,
11399 callback : this.loadResponse,
11403 Roo.applyIf(o, this.conn);
11404 if(this.activeRequest){
11405 Roo.Ajax.abort(this.activeRequest);
11407 this.activeRequest = Roo.Ajax.request(o);
11409 this.conn.request(o);
11412 callback.call(scope||this, null, arg, false);
11417 loadResponse : function(o, success, response){
11418 delete this.activeRequest;
11420 this.fireEvent("loadexception", this, o, response);
11421 o.request.callback.call(o.request.scope, null, o.request.arg, false);
11426 result = o.reader.read(response);
11428 this.fireEvent("loadexception", this, o, response, e);
11429 o.request.callback.call(o.request.scope, null, o.request.arg, false);
11433 this.fireEvent("load", this, o, o.request.arg);
11434 o.request.callback.call(o.request.scope, result, o.request.arg, true);
11438 update : function(dataSet){
11443 updateResponse : function(dataSet){
11448 * Ext JS Library 1.1.1
11449 * Copyright(c) 2006-2007, Ext JS, LLC.
11451 * Originally Released Under LGPL - original licence link has changed is not relivant.
11454 * <script type="text/javascript">
11458 * @class Roo.data.ScriptTagProxy
11459 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11460 * other than the originating domain of the running page.<br><br>
11462 * <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
11463 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11465 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11466 * source code that is used as the source inside a <script> tag.<br><br>
11468 * In order for the browser to process the returned data, the server must wrap the data object
11469 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11470 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11471 * depending on whether the callback name was passed:
11474 boolean scriptTag = false;
11475 String cb = request.getParameter("callback");
11478 response.setContentType("text/javascript");
11480 response.setContentType("application/x-json");
11482 Writer out = response.getWriter();
11484 out.write(cb + "(");
11486 out.print(dataBlock.toJsonString());
11493 * @param {Object} config A configuration object.
11495 Roo.data.ScriptTagProxy = function(config){
11496 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11497 Roo.apply(this, config);
11498 this.head = document.getElementsByTagName("head")[0];
11501 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11503 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11505 * @cfg {String} url The URL from which to request the data object.
11508 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11512 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11513 * the server the name of the callback function set up by the load call to process the returned data object.
11514 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11515 * javascript output which calls this named function passing the data object as its only parameter.
11517 callbackParam : "callback",
11519 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11520 * name to the request.
11525 * Load data from the configured URL, read the data object into
11526 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11527 * process that block using the passed callback.
11528 * @param {Object} params An object containing properties which are to be used as HTTP parameters
11529 * for the request to the remote server.
11530 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11531 * object into a block of Roo.data.Records.
11532 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11533 * The function must be passed <ul>
11534 * <li>The Record block object</li>
11535 * <li>The "arg" argument from the load function</li>
11536 * <li>A boolean success indicator</li>
11538 * @param {Object} scope The scope in which to call the callback
11539 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11541 load : function(params, reader, callback, scope, arg){
11542 if(this.fireEvent("beforeload", this, params) !== false){
11544 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11546 var url = this.url;
11547 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11549 url += "&_dc=" + (new Date().getTime());
11551 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11554 cb : "stcCallback"+transId,
11555 scriptId : "stcScript"+transId,
11559 callback : callback,
11565 window[trans.cb] = function(o){
11566 conn.handleResponse(o, trans);
11569 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11571 if(this.autoAbort !== false){
11575 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11577 var script = document.createElement("script");
11578 script.setAttribute("src", url);
11579 script.setAttribute("type", "text/javascript");
11580 script.setAttribute("id", trans.scriptId);
11581 this.head.appendChild(script);
11583 this.trans = trans;
11585 callback.call(scope||this, null, arg, false);
11590 isLoading : function(){
11591 return this.trans ? true : false;
11595 * Abort the current server request.
11597 abort : function(){
11598 if(this.isLoading()){
11599 this.destroyTrans(this.trans);
11604 destroyTrans : function(trans, isLoaded){
11605 this.head.removeChild(document.getElementById(trans.scriptId));
11606 clearTimeout(trans.timeoutId);
11608 window[trans.cb] = undefined;
11610 delete window[trans.cb];
11613 // if hasn't been loaded, wait for load to remove it to prevent script error
11614 window[trans.cb] = function(){
11615 window[trans.cb] = undefined;
11617 delete window[trans.cb];
11624 handleResponse : function(o, trans){
11625 this.trans = false;
11626 this.destroyTrans(trans, true);
11629 result = trans.reader.readRecords(o);
11631 this.fireEvent("loadexception", this, o, trans.arg, e);
11632 trans.callback.call(trans.scope||window, null, trans.arg, false);
11635 this.fireEvent("load", this, o, trans.arg);
11636 trans.callback.call(trans.scope||window, result, trans.arg, true);
11640 handleFailure : function(trans){
11641 this.trans = false;
11642 this.destroyTrans(trans, false);
11643 this.fireEvent("loadexception", this, null, trans.arg);
11644 trans.callback.call(trans.scope||window, null, trans.arg, false);
11648 * Ext JS Library 1.1.1
11649 * Copyright(c) 2006-2007, Ext JS, LLC.
11651 * Originally Released Under LGPL - original licence link has changed is not relivant.
11654 * <script type="text/javascript">
11658 * @class Roo.data.JsonReader
11659 * @extends Roo.data.DataReader
11660 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11661 * based on mappings in a provided Roo.data.Record constructor.
11663 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11664 * in the reply previously.
11669 var RecordDef = Roo.data.Record.create([
11670 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
11671 {name: 'occupation'} // This field will use "occupation" as the mapping.
11673 var myReader = new Roo.data.JsonReader({
11674 totalProperty: "results", // The property which contains the total dataset size (optional)
11675 root: "rows", // The property which contains an Array of row objects
11676 id: "id" // The property within each row object that provides an ID for the record (optional)
11680 * This would consume a JSON file like this:
11682 { 'results': 2, 'rows': [
11683 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11684 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11687 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11688 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11689 * paged from the remote server.
11690 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11691 * @cfg {String} root name of the property which contains the Array of row objects.
11692 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11693 * @cfg {Array} fields Array of field definition objects
11695 * Create a new JsonReader
11696 * @param {Object} meta Metadata configuration options
11697 * @param {Object} recordType Either an Array of field definition objects,
11698 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11700 Roo.data.JsonReader = function(meta, recordType){
11703 // set some defaults:
11704 Roo.applyIf(meta, {
11705 totalProperty: 'total',
11706 successProperty : 'success',
11711 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11713 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11716 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
11717 * Used by Store query builder to append _requestMeta to params.
11720 metaFromRemote : false,
11722 * This method is only used by a DataProxy which has retrieved data from a remote server.
11723 * @param {Object} response The XHR object which contains the JSON data in its responseText.
11724 * @return {Object} data A data block which is used by an Roo.data.Store object as
11725 * a cache of Roo.data.Records.
11727 read : function(response){
11728 var json = response.responseText;
11730 var o = /* eval:var:o */ eval("("+json+")");
11732 throw {message: "JsonReader.read: Json object not found"};
11738 this.metaFromRemote = true;
11739 this.meta = o.metaData;
11740 this.recordType = Roo.data.Record.create(o.metaData.fields);
11741 this.onMetaChange(this.meta, this.recordType, o);
11743 return this.readRecords(o);
11746 // private function a store will implement
11747 onMetaChange : function(meta, recordType, o){
11754 simpleAccess: function(obj, subsc) {
11761 getJsonAccessor: function(){
11763 return function(expr) {
11765 return(re.test(expr))
11766 ? new Function("obj", "return obj." + expr)
11771 return Roo.emptyFn;
11776 * Create a data block containing Roo.data.Records from an XML document.
11777 * @param {Object} o An object which contains an Array of row objects in the property specified
11778 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11779 * which contains the total size of the dataset.
11780 * @return {Object} data A data block which is used by an Roo.data.Store object as
11781 * a cache of Roo.data.Records.
11783 readRecords : function(o){
11785 * After any data loads, the raw JSON data is available for further custom processing.
11789 var s = this.meta, Record = this.recordType,
11790 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11792 // Generate extraction functions for the totalProperty, the root, the id, and for each field
11794 if(s.totalProperty) {
11795 this.getTotal = this.getJsonAccessor(s.totalProperty);
11797 if(s.successProperty) {
11798 this.getSuccess = this.getJsonAccessor(s.successProperty);
11800 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11802 var g = this.getJsonAccessor(s.id);
11803 this.getId = function(rec) {
11805 return (r === undefined || r === "") ? null : r;
11808 this.getId = function(){return null;};
11811 for(var jj = 0; jj < fl; jj++){
11813 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11814 this.ef[jj] = this.getJsonAccessor(map);
11818 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11819 if(s.totalProperty){
11820 var vt = parseInt(this.getTotal(o), 10);
11825 if(s.successProperty){
11826 var vs = this.getSuccess(o);
11827 if(vs === false || vs === 'false'){
11832 for(var i = 0; i < c; i++){
11835 var id = this.getId(n);
11836 for(var j = 0; j < fl; j++){
11838 var v = this.ef[j](n);
11840 Roo.log('missing convert for ' + f.name);
11844 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11846 var record = new Record(values, id);
11848 records[i] = record;
11854 totalRecords : totalRecords
11859 * Ext JS Library 1.1.1
11860 * Copyright(c) 2006-2007, Ext JS, LLC.
11862 * Originally Released Under LGPL - original licence link has changed is not relivant.
11865 * <script type="text/javascript">
11869 * @class Roo.data.ArrayReader
11870 * @extends Roo.data.DataReader
11871 * Data reader class to create an Array of Roo.data.Record objects from an Array.
11872 * Each element of that Array represents a row of data fields. The
11873 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11874 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11878 var RecordDef = Roo.data.Record.create([
11879 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
11880 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
11882 var myReader = new Roo.data.ArrayReader({
11883 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
11887 * This would consume an Array like this:
11889 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11891 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11893 * Create a new JsonReader
11894 * @param {Object} meta Metadata configuration options.
11895 * @param {Object} recordType Either an Array of field definition objects
11896 * as specified to {@link Roo.data.Record#create},
11897 * or an {@link Roo.data.Record} object
11898 * created using {@link Roo.data.Record#create}.
11900 Roo.data.ArrayReader = function(meta, recordType){
11901 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11904 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11906 * Create a data block containing Roo.data.Records from an XML document.
11907 * @param {Object} o An Array of row objects which represents the dataset.
11908 * @return {Object} data A data block which is used by an Roo.data.Store object as
11909 * a cache of Roo.data.Records.
11911 readRecords : function(o){
11912 var sid = this.meta ? this.meta.id : null;
11913 var recordType = this.recordType, fields = recordType.prototype.fields;
11916 for(var i = 0; i < root.length; i++){
11919 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11920 for(var j = 0, jlen = fields.length; j < jlen; j++){
11921 var f = fields.items[j];
11922 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11923 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11925 values[f.name] = v;
11927 var record = new recordType(values, id);
11929 records[records.length] = record;
11933 totalRecords : records.length
11942 * @class Roo.bootstrap.ComboBox
11943 * @extends Roo.bootstrap.TriggerField
11944 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11945 * @cfg {Boolean} append (true|false) default false
11946 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11947 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11948 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11949 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11950 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11951 * @cfg {Boolean} animate default true
11952 * @cfg {Boolean} emptyResultText only for touch device
11953 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11955 * Create a new ComboBox.
11956 * @param {Object} config Configuration options
11958 Roo.bootstrap.ComboBox = function(config){
11959 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11963 * Fires when the dropdown list is expanded
11964 * @param {Roo.bootstrap.ComboBox} combo This combo box
11969 * Fires when the dropdown list is collapsed
11970 * @param {Roo.bootstrap.ComboBox} combo This combo box
11974 * @event beforeselect
11975 * Fires before a list item is selected. Return false to cancel the selection.
11976 * @param {Roo.bootstrap.ComboBox} combo This combo box
11977 * @param {Roo.data.Record} record The data record returned from the underlying store
11978 * @param {Number} index The index of the selected item in the dropdown list
11980 'beforeselect' : true,
11983 * Fires when a list item is selected
11984 * @param {Roo.bootstrap.ComboBox} combo This combo box
11985 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11986 * @param {Number} index The index of the selected item in the dropdown list
11990 * @event beforequery
11991 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11992 * The event object passed has these properties:
11993 * @param {Roo.bootstrap.ComboBox} combo This combo box
11994 * @param {String} query The query
11995 * @param {Boolean} forceAll true to force "all" query
11996 * @param {Boolean} cancel true to cancel the query
11997 * @param {Object} e The query event object
11999 'beforequery': true,
12002 * Fires when the 'add' icon is pressed (add a listener to enable add button)
12003 * @param {Roo.bootstrap.ComboBox} combo This combo box
12008 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12009 * @param {Roo.bootstrap.ComboBox} combo This combo box
12010 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12015 * Fires when the remove value from the combobox array
12016 * @param {Roo.bootstrap.ComboBox} combo This combo box
12020 * @event afterremove
12021 * Fires when the remove value from the combobox array
12022 * @param {Roo.bootstrap.ComboBox} combo This combo box
12024 'afterremove' : true,
12026 * @event specialfilter
12027 * Fires when specialfilter
12028 * @param {Roo.bootstrap.ComboBox} combo This combo box
12030 'specialfilter' : true,
12033 * Fires when tick the element
12034 * @param {Roo.bootstrap.ComboBox} combo This combo box
12038 * @event touchviewdisplay
12039 * Fires when touch view require special display (default is using displayField)
12040 * @param {Roo.bootstrap.ComboBox} combo This combo box
12041 * @param {Object} cfg set html .
12043 'touchviewdisplay' : true
12048 this.tickItems = [];
12050 this.selectedIndex = -1;
12051 if(this.mode == 'local'){
12052 if(config.queryDelay === undefined){
12053 this.queryDelay = 10;
12055 if(config.minChars === undefined){
12061 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12064 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12065 * rendering into an Roo.Editor, defaults to false)
12068 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12069 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12072 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12075 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12076 * the dropdown list (defaults to undefined, with no header element)
12080 * @cfg {String/Roo.Template} tpl The template to use to render the output
12084 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12086 listWidth: undefined,
12088 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12089 * mode = 'remote' or 'text' if mode = 'local')
12091 displayField: undefined,
12094 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12095 * mode = 'remote' or 'value' if mode = 'local').
12096 * Note: use of a valueField requires the user make a selection
12097 * in order for a value to be mapped.
12099 valueField: undefined,
12101 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12106 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12107 * field's data value (defaults to the underlying DOM element's name)
12109 hiddenName: undefined,
12111 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12115 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12117 selectedClass: 'active',
12120 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12124 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12125 * anchor positions (defaults to 'tl-bl')
12127 listAlign: 'tl-bl?',
12129 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12133 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
12134 * query specified by the allQuery config option (defaults to 'query')
12136 triggerAction: 'query',
12138 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12139 * (defaults to 4, does not apply if editable = false)
12143 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12144 * delay (typeAheadDelay) if it matches a known value (defaults to false)
12148 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12149 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12153 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12154 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
12158 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
12159 * when editable = true (defaults to false)
12161 selectOnFocus:false,
12163 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12165 queryParam: 'query',
12167 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
12168 * when mode = 'remote' (defaults to 'Loading...')
12170 loadingText: 'Loading...',
12172 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12176 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12180 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12181 * traditional select (defaults to true)
12185 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12189 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12193 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12194 * listWidth has a higher value)
12198 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12199 * allow the user to set arbitrary text into the field (defaults to false)
12201 forceSelection:false,
12203 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12204 * if typeAhead = true (defaults to 250)
12206 typeAheadDelay : 250,
12208 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12209 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12211 valueNotFoundText : undefined,
12213 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12215 blockFocus : false,
12218 * @cfg {Boolean} disableClear Disable showing of clear button.
12220 disableClear : false,
12222 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
12224 alwaysQuery : false,
12227 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
12232 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12234 invalidClass : "has-warning",
12237 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12239 validClass : "has-success",
12242 * @cfg {Boolean} specialFilter (true|false) special filter default false
12244 specialFilter : false,
12247 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12249 mobileTouchView : true,
12252 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12254 useNativeIOS : false,
12256 ios_options : false,
12268 btnPosition : 'right',
12269 triggerList : true,
12270 showToggleBtn : true,
12272 emptyResultText: 'Empty',
12273 triggerText : 'Select',
12275 // element that contains real text value.. (when hidden is used..)
12277 getAutoCreate : function()
12282 * Render classic select for iso
12285 if(Roo.isIOS && this.useNativeIOS){
12286 cfg = this.getAutoCreateNativeIOS();
12294 if(Roo.isTouch && this.mobileTouchView){
12295 cfg = this.getAutoCreateTouchView();
12302 if(!this.tickable){
12303 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12308 * ComboBox with tickable selections
12311 var align = this.labelAlign || this.parentLabelAlign();
12314 cls : 'form-group roo-combobox-tickable' //input-group
12319 cls : 'tickable-buttons',
12324 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12325 html : this.triggerText
12331 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12338 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12345 buttons.cn.unshift({
12347 cls: 'roo-select2-search-field-input'
12353 Roo.each(buttons.cn, function(c){
12355 c.cls += ' btn-' + _this.size;
12358 if (_this.disabled) {
12369 cls: 'form-hidden-field'
12373 cls: 'roo-select2-choices',
12377 cls: 'roo-select2-search-field',
12389 cls: 'roo-select2-container input-group roo-select2-container-multi',
12394 // cls: 'typeahead typeahead-long dropdown-menu',
12395 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
12400 if(this.hasFeedback && !this.allowBlank){
12404 cls: 'glyphicon form-control-feedback'
12407 combobox.cn.push(feedback);
12410 if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
12412 // Roo.log("left and has label");
12416 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12417 tooltip : 'This field is required'
12422 cls : 'control-label col-sm-' + this.labelWidth,
12423 html : this.fieldLabel
12427 cls : "col-sm-" + (12 - this.labelWidth),
12435 if(this.indicatorpos == 'right'){
12441 cls : 'control-label col-sm-' + this.labelWidth,
12442 html : this.fieldLabel
12447 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12448 tooltip : 'This field is required'
12451 cls : "col-sm-" + (12 - this.labelWidth),
12462 } else if ( this.fieldLabel.length) {
12463 // Roo.log(" label");
12467 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12468 tooltip : 'This field is required'
12472 //cls : 'input-group-addon',
12473 html : this.fieldLabel
12481 if(this.indicatorpos == 'right'){
12486 //cls : 'input-group-addon',
12487 html : this.fieldLabel
12493 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12494 tooltip : 'This field is required'
12505 // Roo.log(" no label && no align");
12512 ['xs','sm','md','lg'].map(function(size){
12513 if (settings[size]) {
12514 cfg.cls += ' col-' + size + '-' + settings[size];
12522 _initEventsCalled : false,
12525 initEvents: function()
12527 if (this._initEventsCalled) { // as we call render... prevent looping...
12530 this._initEventsCalled = true;
12533 throw "can not find store for combo";
12536 this.store = Roo.factory(this.store, Roo.data);
12538 // if we are building from html. then this element is so complex, that we can not really
12539 // use the rendered HTML.
12540 // so we have to trash and replace the previous code.
12541 if (Roo.XComponent.build_from_html) {
12543 // remove this element....
12544 var e = this.el.dom, k=0;
12545 while (e ) { e = e.previousSibling; ++k;}
12550 this.rendered = false;
12552 this.render(this.parent().getChildContainer(true), k);
12558 if(Roo.isIOS && this.useNativeIOS){
12559 this.initIOSView();
12567 if(Roo.isTouch && this.mobileTouchView){
12568 this.initTouchView();
12573 this.initTickableEvents();
12577 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
12579 if(this.hiddenName){
12581 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12583 this.hiddenField.dom.value =
12584 this.hiddenValue !== undefined ? this.hiddenValue :
12585 this.value !== undefined ? this.value : '';
12587 // prevent input submission
12588 this.el.dom.removeAttribute('name');
12589 this.hiddenField.dom.setAttribute('name', this.hiddenName);
12594 // this.el.dom.setAttribute('autocomplete', 'off');
12597 var cls = 'x-combo-list';
12599 //this.list = new Roo.Layer({
12600 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12606 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12607 _this.list.setWidth(lw);
12610 this.list.on('mouseover', this.onViewOver, this);
12611 this.list.on('mousemove', this.onViewMove, this);
12613 this.list.on('scroll', this.onViewScroll, this);
12616 this.list.swallowEvent('mousewheel');
12617 this.assetHeight = 0;
12620 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12621 this.assetHeight += this.header.getHeight();
12624 this.innerList = this.list.createChild({cls:cls+'-inner'});
12625 this.innerList.on('mouseover', this.onViewOver, this);
12626 this.innerList.on('mousemove', this.onViewMove, this);
12627 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12629 if(this.allowBlank && !this.pageSize && !this.disableClear){
12630 this.footer = this.list.createChild({cls:cls+'-ft'});
12631 this.pageTb = new Roo.Toolbar(this.footer);
12635 this.footer = this.list.createChild({cls:cls+'-ft'});
12636 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12637 {pageSize: this.pageSize});
12641 if (this.pageTb && this.allowBlank && !this.disableClear) {
12643 this.pageTb.add(new Roo.Toolbar.Fill(), {
12644 cls: 'x-btn-icon x-btn-clear',
12646 handler: function()
12649 _this.clearValue();
12650 _this.onSelect(false, -1);
12655 this.assetHeight += this.footer.getHeight();
12660 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12663 this.view = new Roo.View(this.list, this.tpl, {
12664 singleSelect:true, store: this.store, selectedClass: this.selectedClass
12666 //this.view.wrapEl.setDisplayed(false);
12667 this.view.on('click', this.onViewClick, this);
12671 this.store.on('beforeload', this.onBeforeLoad, this);
12672 this.store.on('load', this.onLoad, this);
12673 this.store.on('loadexception', this.onLoadException, this);
12675 if(this.resizable){
12676 this.resizer = new Roo.Resizable(this.list, {
12677 pinned:true, handles:'se'
12679 this.resizer.on('resize', function(r, w, h){
12680 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12681 this.listWidth = w;
12682 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12683 this.restrictHeight();
12685 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12688 if(!this.editable){
12689 this.editable = true;
12690 this.setEditable(false);
12695 if (typeof(this.events.add.listeners) != 'undefined') {
12697 this.addicon = this.wrap.createChild(
12698 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
12700 this.addicon.on('click', function(e) {
12701 this.fireEvent('add', this);
12704 if (typeof(this.events.edit.listeners) != 'undefined') {
12706 this.editicon = this.wrap.createChild(
12707 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
12708 if (this.addicon) {
12709 this.editicon.setStyle('margin-left', '40px');
12711 this.editicon.on('click', function(e) {
12713 // we fire even if inothing is selected..
12714 this.fireEvent('edit', this, this.lastData );
12720 this.keyNav = new Roo.KeyNav(this.inputEl(), {
12721 "up" : function(e){
12722 this.inKeyMode = true;
12726 "down" : function(e){
12727 if(!this.isExpanded()){
12728 this.onTriggerClick();
12730 this.inKeyMode = true;
12735 "enter" : function(e){
12736 // this.onViewClick();
12740 if(this.fireEvent("specialkey", this, e)){
12741 this.onViewClick(false);
12747 "esc" : function(e){
12751 "tab" : function(e){
12754 if(this.fireEvent("specialkey", this, e)){
12755 this.onViewClick(false);
12763 doRelay : function(foo, bar, hname){
12764 if(hname == 'down' || this.scope.isExpanded()){
12765 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12774 this.queryDelay = Math.max(this.queryDelay || 10,
12775 this.mode == 'local' ? 10 : 250);
12778 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12780 if(this.typeAhead){
12781 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12783 if(this.editable !== false){
12784 this.inputEl().on("keyup", this.onKeyUp, this);
12786 if(this.forceSelection){
12787 this.inputEl().on('blur', this.doForce, this);
12791 this.choices = this.el.select('ul.roo-select2-choices', true).first();
12792 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12796 initTickableEvents: function()
12800 if(this.hiddenName){
12802 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12804 this.hiddenField.dom.value =
12805 this.hiddenValue !== undefined ? this.hiddenValue :
12806 this.value !== undefined ? this.value : '';
12808 // prevent input submission
12809 this.el.dom.removeAttribute('name');
12810 this.hiddenField.dom.setAttribute('name', this.hiddenName);
12815 // this.list = this.el.select('ul.dropdown-menu',true).first();
12817 this.choices = this.el.select('ul.roo-select2-choices', true).first();
12818 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12819 if(this.triggerList){
12820 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12823 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12824 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12826 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12827 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12829 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12830 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12832 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12833 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12834 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12837 this.cancelBtn.hide();
12842 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12843 _this.list.setWidth(lw);
12846 this.list.on('mouseover', this.onViewOver, this);
12847 this.list.on('mousemove', this.onViewMove, this);
12849 this.list.on('scroll', this.onViewScroll, this);
12852 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>';
12855 this.view = new Roo.View(this.list, this.tpl, {
12856 singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12859 //this.view.wrapEl.setDisplayed(false);
12860 this.view.on('click', this.onViewClick, this);
12864 this.store.on('beforeload', this.onBeforeLoad, this);
12865 this.store.on('load', this.onLoad, this);
12866 this.store.on('loadexception', this.onLoadException, this);
12869 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12870 "up" : function(e){
12871 this.inKeyMode = true;
12875 "down" : function(e){
12876 this.inKeyMode = true;
12880 "enter" : function(e){
12881 if(this.fireEvent("specialkey", this, e)){
12882 this.onViewClick(false);
12888 "esc" : function(e){
12889 this.onTickableFooterButtonClick(e, false, false);
12892 "tab" : function(e){
12893 this.fireEvent("specialkey", this, e);
12895 this.onTickableFooterButtonClick(e, false, false);
12902 doRelay : function(e, fn, key){
12903 if(this.scope.isExpanded()){
12904 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12913 this.queryDelay = Math.max(this.queryDelay || 10,
12914 this.mode == 'local' ? 10 : 250);
12917 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12919 if(this.typeAhead){
12920 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12923 if(this.editable !== false){
12924 this.tickableInputEl().on("keyup", this.onKeyUp, this);
12929 onDestroy : function(){
12931 this.view.setStore(null);
12932 this.view.el.removeAllListeners();
12933 this.view.el.remove();
12934 this.view.purgeListeners();
12937 this.list.dom.innerHTML = '';
12941 this.store.un('beforeload', this.onBeforeLoad, this);
12942 this.store.un('load', this.onLoad, this);
12943 this.store.un('loadexception', this.onLoadException, this);
12945 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12949 fireKey : function(e){
12950 if(e.isNavKeyPress() && !this.list.isVisible()){
12951 this.fireEvent("specialkey", this, e);
12956 onResize: function(w, h){
12957 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12959 // if(typeof w != 'number'){
12960 // // we do not handle it!?!?
12963 // var tw = this.trigger.getWidth();
12964 // // tw += this.addicon ? this.addicon.getWidth() : 0;
12965 // // tw += this.editicon ? this.editicon.getWidth() : 0;
12967 // this.inputEl().setWidth( this.adjustWidth('input', x));
12969 // //this.trigger.setStyle('left', x+'px');
12971 // if(this.list && this.listWidth === undefined){
12972 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12973 // this.list.setWidth(lw);
12974 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12982 * Allow or prevent the user from directly editing the field text. If false is passed,
12983 * the user will only be able to select from the items defined in the dropdown list. This method
12984 * is the runtime equivalent of setting the 'editable' config option at config time.
12985 * @param {Boolean} value True to allow the user to directly edit the field text
12987 setEditable : function(value){
12988 if(value == this.editable){
12991 this.editable = value;
12993 this.inputEl().dom.setAttribute('readOnly', true);
12994 this.inputEl().on('mousedown', this.onTriggerClick, this);
12995 this.inputEl().addClass('x-combo-noedit');
12997 this.inputEl().dom.setAttribute('readOnly', false);
12998 this.inputEl().un('mousedown', this.onTriggerClick, this);
12999 this.inputEl().removeClass('x-combo-noedit');
13005 onBeforeLoad : function(combo,opts){
13006 if(!this.hasFocus){
13010 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13012 this.restrictHeight();
13013 this.selectedIndex = -1;
13017 onLoad : function(){
13019 this.hasQuery = false;
13021 if(!this.hasFocus){
13025 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13026 this.loading.hide();
13029 if(this.store.getCount() > 0){
13031 this.restrictHeight();
13032 if(this.lastQuery == this.allQuery){
13033 if(this.editable && !this.tickable){
13034 this.inputEl().dom.select();
13038 !this.selectByValue(this.value, true) &&
13041 !this.store.lastOptions ||
13042 typeof(this.store.lastOptions.add) == 'undefined' ||
13043 this.store.lastOptions.add != true
13046 this.select(0, true);
13049 if(this.autoFocus){
13052 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13053 this.taTask.delay(this.typeAheadDelay);
13057 this.onEmptyResults();
13063 onLoadException : function()
13065 this.hasQuery = false;
13067 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13068 this.loading.hide();
13071 if(this.tickable && this.editable){
13076 // only causes errors at present
13077 //Roo.log(this.store.reader.jsonData);
13078 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13080 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13086 onTypeAhead : function(){
13087 if(this.store.getCount() > 0){
13088 var r = this.store.getAt(0);
13089 var newValue = r.data[this.displayField];
13090 var len = newValue.length;
13091 var selStart = this.getRawValue().length;
13093 if(selStart != len){
13094 this.setRawValue(newValue);
13095 this.selectText(selStart, newValue.length);
13101 onSelect : function(record, index){
13103 if(this.fireEvent('beforeselect', this, record, index) !== false){
13105 this.setFromData(index > -1 ? record.data : false);
13108 this.fireEvent('select', this, record, index);
13113 * Returns the currently selected field value or empty string if no value is set.
13114 * @return {String} value The selected value
13116 getValue : function()
13118 if(Roo.isIOS && this.useNativeIOS){
13119 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13123 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13126 if(this.valueField){
13127 return typeof this.value != 'undefined' ? this.value : '';
13129 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13133 getRawValue : function()
13135 if(Roo.isIOS && this.useNativeIOS){
13136 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13139 var v = this.inputEl().getValue();
13145 * Clears any text/value currently set in the field
13147 clearValue : function(){
13149 if(this.hiddenField){
13150 this.hiddenField.dom.value = '';
13153 this.setRawValue('');
13154 this.lastSelectionText = '';
13155 this.lastData = false;
13157 var close = this.closeTriggerEl();
13168 * Sets the specified value into the field. If the value finds a match, the corresponding record text
13169 * will be displayed in the field. If the value does not match the data value of an existing item,
13170 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13171 * Otherwise the field will be blank (although the value will still be set).
13172 * @param {String} value The value to match
13174 setValue : function(v)
13176 if(Roo.isIOS && this.useNativeIOS){
13177 this.setIOSValue(v);
13187 if(this.valueField){
13188 var r = this.findRecord(this.valueField, v);
13190 text = r.data[this.displayField];
13191 }else if(this.valueNotFoundText !== undefined){
13192 text = this.valueNotFoundText;
13195 this.lastSelectionText = text;
13196 if(this.hiddenField){
13197 this.hiddenField.dom.value = v;
13199 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13202 var close = this.closeTriggerEl();
13205 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13211 * @property {Object} the last set data for the element
13216 * Sets the value of the field based on a object which is related to the record format for the store.
13217 * @param {Object} value the value to set as. or false on reset?
13219 setFromData : function(o){
13226 var dv = ''; // display value
13227 var vv = ''; // value value..
13229 if (this.displayField) {
13230 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13232 // this is an error condition!!!
13233 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
13236 if(this.valueField){
13237 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13240 var close = this.closeTriggerEl();
13243 (vv.length || vv * 1 > 0) ? close.show() : close.hide();
13246 if(this.hiddenField){
13247 this.hiddenField.dom.value = vv;
13249 this.lastSelectionText = dv;
13250 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13254 // no hidden field.. - we store the value in 'value', but still display
13255 // display field!!!!
13256 this.lastSelectionText = dv;
13257 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13264 reset : function(){
13265 // overridden so that last data is reset..
13272 this.setValue(this.originalValue);
13273 //this.clearInvalid();
13274 this.lastData = false;
13276 this.view.clearSelections();
13282 findRecord : function(prop, value){
13284 if(this.store.getCount() > 0){
13285 this.store.each(function(r){
13286 if(r.data[prop] == value){
13296 getName: function()
13298 // returns hidden if it's set..
13299 if (!this.rendered) {return ''};
13300 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
13304 onViewMove : function(e, t){
13305 this.inKeyMode = false;
13309 onViewOver : function(e, t){
13310 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13313 var item = this.view.findItemFromChild(t);
13316 var index = this.view.indexOf(item);
13317 this.select(index, false);
13322 onViewClick : function(view, doFocus, el, e)
13324 var index = this.view.getSelectedIndexes()[0];
13326 var r = this.store.getAt(index);
13330 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13337 Roo.each(this.tickItems, function(v,k){
13339 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13341 _this.tickItems.splice(k, 1);
13343 if(typeof(e) == 'undefined' && view == false){
13344 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13356 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13357 this.tickItems.push(r.data);
13360 if(typeof(e) == 'undefined' && view == false){
13361 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13368 this.onSelect(r, index);
13370 if(doFocus !== false && !this.blockFocus){
13371 this.inputEl().focus();
13376 restrictHeight : function(){
13377 //this.innerList.dom.style.height = '';
13378 //var inner = this.innerList.dom;
13379 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13380 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13381 //this.list.beginUpdate();
13382 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13383 this.list.alignTo(this.inputEl(), this.listAlign);
13384 this.list.alignTo(this.inputEl(), this.listAlign);
13385 //this.list.endUpdate();
13389 onEmptyResults : function(){
13391 if(this.tickable && this.editable){
13392 this.restrictHeight();
13400 * Returns true if the dropdown list is expanded, else false.
13402 isExpanded : function(){
13403 return this.list.isVisible();
13407 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13408 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13409 * @param {String} value The data value of the item to select
13410 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13411 * selected item if it is not currently in view (defaults to true)
13412 * @return {Boolean} True if the value matched an item in the list, else false
13414 selectByValue : function(v, scrollIntoView){
13415 if(v !== undefined && v !== null){
13416 var r = this.findRecord(this.valueField || this.displayField, v);
13418 this.select(this.store.indexOf(r), scrollIntoView);
13426 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13427 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13428 * @param {Number} index The zero-based index of the list item to select
13429 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13430 * selected item if it is not currently in view (defaults to true)
13432 select : function(index, scrollIntoView){
13433 this.selectedIndex = index;
13434 this.view.select(index);
13435 if(scrollIntoView !== false){
13436 var el = this.view.getNode(index);
13438 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13441 this.list.scrollChildIntoView(el, false);
13447 selectNext : function(){
13448 var ct = this.store.getCount();
13450 if(this.selectedIndex == -1){
13452 }else if(this.selectedIndex < ct-1){
13453 this.select(this.selectedIndex+1);
13459 selectPrev : function(){
13460 var ct = this.store.getCount();
13462 if(this.selectedIndex == -1){
13464 }else if(this.selectedIndex != 0){
13465 this.select(this.selectedIndex-1);
13471 onKeyUp : function(e){
13472 if(this.editable !== false && !e.isSpecialKey()){
13473 this.lastKey = e.getKey();
13474 this.dqTask.delay(this.queryDelay);
13479 validateBlur : function(){
13480 return !this.list || !this.list.isVisible();
13484 initQuery : function(){
13486 var v = this.getRawValue();
13488 if(this.tickable && this.editable){
13489 v = this.tickableInputEl().getValue();
13496 doForce : function(){
13497 if(this.inputEl().dom.value.length > 0){
13498 this.inputEl().dom.value =
13499 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
13505 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
13506 * query allowing the query action to be canceled if needed.
13507 * @param {String} query The SQL query to execute
13508 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
13509 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
13510 * saved in the current store (defaults to false)
13512 doQuery : function(q, forceAll){
13514 if(q === undefined || q === null){
13519 forceAll: forceAll,
13523 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
13528 forceAll = qe.forceAll;
13529 if(forceAll === true || (q.length >= this.minChars)){
13531 this.hasQuery = true;
13533 if(this.lastQuery != q || this.alwaysQuery){
13534 this.lastQuery = q;
13535 if(this.mode == 'local'){
13536 this.selectedIndex = -1;
13538 this.store.clearFilter();
13541 if(this.specialFilter){
13542 this.fireEvent('specialfilter', this);
13547 this.store.filter(this.displayField, q);
13550 this.store.fireEvent("datachanged", this.store);
13557 this.store.baseParams[this.queryParam] = q;
13559 var options = {params : this.getParams(q)};
13562 options.add = true;
13563 options.params.start = this.page * this.pageSize;
13566 this.store.load(options);
13569 * this code will make the page width larger, at the beginning, the list not align correctly,
13570 * we should expand the list on onLoad
13571 * so command out it
13576 this.selectedIndex = -1;
13581 this.loadNext = false;
13585 getParams : function(q){
13587 //p[this.queryParam] = q;
13591 p.limit = this.pageSize;
13597 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
13599 collapse : function(){
13600 if(!this.isExpanded()){
13607 this.hasFocus = false;
13609 this.cancelBtn.hide();
13610 this.trigger.show();
13613 this.tickableInputEl().dom.value = '';
13614 this.tickableInputEl().blur();
13619 Roo.get(document).un('mousedown', this.collapseIf, this);
13620 Roo.get(document).un('mousewheel', this.collapseIf, this);
13621 if (!this.editable) {
13622 Roo.get(document).un('keydown', this.listKeyPress, this);
13624 this.fireEvent('collapse', this);
13630 collapseIf : function(e){
13631 var in_combo = e.within(this.el);
13632 var in_list = e.within(this.list);
13633 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13635 if (in_combo || in_list || is_list) {
13636 //e.stopPropagation();
13641 this.onTickableFooterButtonClick(e, false, false);
13649 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13651 expand : function(){
13653 if(this.isExpanded() || !this.hasFocus){
13657 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13658 this.list.setWidth(lw);
13665 this.restrictHeight();
13669 this.tickItems = Roo.apply([], this.item);
13672 this.cancelBtn.show();
13673 this.trigger.hide();
13676 this.tickableInputEl().focus();
13681 Roo.get(document).on('mousedown', this.collapseIf, this);
13682 Roo.get(document).on('mousewheel', this.collapseIf, this);
13683 if (!this.editable) {
13684 Roo.get(document).on('keydown', this.listKeyPress, this);
13687 this.fireEvent('expand', this);
13691 // Implements the default empty TriggerField.onTriggerClick function
13692 onTriggerClick : function(e)
13694 Roo.log('trigger click');
13696 if(this.disabled || !this.triggerList){
13701 this.loadNext = false;
13703 if(this.isExpanded()){
13705 if (!this.blockFocus) {
13706 this.inputEl().focus();
13710 this.hasFocus = true;
13711 if(this.triggerAction == 'all') {
13712 this.doQuery(this.allQuery, true);
13714 this.doQuery(this.getRawValue());
13716 if (!this.blockFocus) {
13717 this.inputEl().focus();
13722 onTickableTriggerClick : function(e)
13729 this.loadNext = false;
13730 this.hasFocus = true;
13732 if(this.triggerAction == 'all') {
13733 this.doQuery(this.allQuery, true);
13735 this.doQuery(this.getRawValue());
13739 onSearchFieldClick : function(e)
13741 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13742 this.onTickableFooterButtonClick(e, false, false);
13746 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13751 this.loadNext = false;
13752 this.hasFocus = true;
13754 if(this.triggerAction == 'all') {
13755 this.doQuery(this.allQuery, true);
13757 this.doQuery(this.getRawValue());
13761 listKeyPress : function(e)
13763 //Roo.log('listkeypress');
13764 // scroll to first matching element based on key pres..
13765 if (e.isSpecialKey()) {
13768 var k = String.fromCharCode(e.getKey()).toUpperCase();
13771 var csel = this.view.getSelectedNodes();
13772 var cselitem = false;
13774 var ix = this.view.indexOf(csel[0]);
13775 cselitem = this.store.getAt(ix);
13776 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13782 this.store.each(function(v) {
13784 // start at existing selection.
13785 if (cselitem.id == v.id) {
13791 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13792 match = this.store.indexOf(v);
13798 if (match === false) {
13799 return true; // no more action?
13802 this.view.select(match);
13803 var sn = Roo.get(this.view.getSelectedNodes()[0]);
13804 sn.scrollIntoView(sn.dom.parentNode, false);
13807 onViewScroll : function(e, t){
13809 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){
13813 this.hasQuery = true;
13815 this.loading = this.list.select('.loading', true).first();
13817 if(this.loading === null){
13818 this.list.createChild({
13820 cls: 'loading roo-select2-more-results roo-select2-active',
13821 html: 'Loading more results...'
13824 this.loading = this.list.select('.loading', true).first();
13826 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13828 this.loading.hide();
13831 this.loading.show();
13836 this.loadNext = true;
13838 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13843 addItem : function(o)
13845 var dv = ''; // display value
13847 if (this.displayField) {
13848 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13850 // this is an error condition!!!
13851 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
13858 var choice = this.choices.createChild({
13860 cls: 'roo-select2-search-choice',
13869 cls: 'roo-select2-search-choice-close',
13874 }, this.searchField);
13876 var close = choice.select('a.roo-select2-search-choice-close', true).first();
13878 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13886 this.inputEl().dom.value = '';
13891 onRemoveItem : function(e, _self, o)
13893 e.preventDefault();
13895 this.lastItem = Roo.apply([], this.item);
13897 var index = this.item.indexOf(o.data) * 1;
13900 Roo.log('not this item?!');
13904 this.item.splice(index, 1);
13909 this.fireEvent('remove', this, e);
13915 syncValue : function()
13917 if(!this.item.length){
13924 Roo.each(this.item, function(i){
13925 if(_this.valueField){
13926 value.push(i[_this.valueField]);
13933 this.value = value.join(',');
13935 if(this.hiddenField){
13936 this.hiddenField.dom.value = this.value;
13939 this.store.fireEvent("datachanged", this.store);
13944 clearItem : function()
13946 if(!this.multiple){
13952 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
13960 if(this.tickable && !Roo.isTouch){
13961 this.view.refresh();
13965 inputEl: function ()
13967 if(Roo.isIOS && this.useNativeIOS){
13968 return this.el.select('select.roo-ios-select', true).first();
13971 if(Roo.isTouch && this.mobileTouchView){
13972 return this.el.select('input.form-control',true).first();
13976 return this.searchField;
13979 return this.el.select('input.form-control',true).first();
13982 onTickableFooterButtonClick : function(e, btn, el)
13984 e.preventDefault();
13986 this.lastItem = Roo.apply([], this.item);
13988 if(btn && btn.name == 'cancel'){
13989 this.tickItems = Roo.apply([], this.item);
13998 Roo.each(this.tickItems, function(o){
14006 validate : function()
14008 var v = this.getRawValue();
14011 v = this.getValue();
14014 if(this.disabled || this.allowBlank || v.length){
14019 this.markInvalid();
14023 tickableInputEl : function()
14025 if(!this.tickable || !this.editable){
14026 return this.inputEl();
14029 return this.inputEl().select('.roo-select2-search-field-input', true).first();
14033 getAutoCreateTouchView : function()
14038 cls: 'form-group' //input-group
14044 type : this.inputType,
14045 cls : 'form-control x-combo-noedit',
14046 autocomplete: 'new-password',
14047 placeholder : this.placeholder || '',
14052 input.name = this.name;
14056 input.cls += ' input-' + this.size;
14059 if (this.disabled) {
14060 input.disabled = true;
14071 inputblock.cls += ' input-group';
14073 inputblock.cn.unshift({
14075 cls : 'input-group-addon',
14080 if(this.removable && !this.multiple){
14081 inputblock.cls += ' roo-removable';
14083 inputblock.cn.push({
14086 cls : 'roo-combo-removable-btn close'
14090 if(this.hasFeedback && !this.allowBlank){
14092 inputblock.cls += ' has-feedback';
14094 inputblock.cn.push({
14096 cls: 'glyphicon form-control-feedback'
14103 inputblock.cls += (this.before) ? '' : ' input-group';
14105 inputblock.cn.push({
14107 cls : 'input-group-addon',
14118 cls: 'form-hidden-field'
14132 cls: 'form-hidden-field'
14136 cls: 'roo-select2-choices',
14140 cls: 'roo-select2-search-field',
14153 cls: 'roo-select2-container input-group roo-touchview-combobox ',
14159 if(!this.multiple && this.showToggleBtn){
14166 if (this.caret != false) {
14169 cls: 'fa fa-' + this.caret
14176 cls : 'input-group-addon btn dropdown-toggle',
14181 cls: 'combobox-clear',
14195 combobox.cls += ' roo-select2-container-multi';
14198 var align = this.labelAlign || this.parentLabelAlign();
14202 if(this.fieldLabel.length && this.labelWidth){
14204 var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
14205 var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
14210 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14211 tooltip : 'This field is required'
14215 cls : 'control-label ' + lw,
14216 html : this.fieldLabel
14227 if(this.indicatorpos == 'right'){
14231 cls : 'control-label ' + lw,
14232 html : this.fieldLabel
14237 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14238 tooltip : 'This field is required'
14250 var settings = this;
14252 ['xs','sm','md','lg'].map(function(size){
14253 if (settings[size]) {
14254 cfg.cls += ' col-' + size + '-' + settings[size];
14261 initTouchView : function()
14263 this.renderTouchView();
14265 this.touchViewEl.on('scroll', function(){
14266 this.el.dom.scrollTop = 0;
14269 this.originalValue = this.getValue();
14271 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14273 this.inputEl().on("click", this.showTouchView, this);
14274 if (this.triggerEl) {
14275 this.triggerEl.on("click", this.showTouchView, this);
14279 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14280 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14282 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14284 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14285 this.store.on('load', this.onTouchViewLoad, this);
14286 this.store.on('loadexception', this.onTouchViewLoadException, this);
14288 if(this.hiddenName){
14290 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14292 this.hiddenField.dom.value =
14293 this.hiddenValue !== undefined ? this.hiddenValue :
14294 this.value !== undefined ? this.value : '';
14296 this.el.dom.removeAttribute('name');
14297 this.hiddenField.dom.setAttribute('name', this.hiddenName);
14301 this.choices = this.el.select('ul.roo-select2-choices', true).first();
14302 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14305 if(this.removable && !this.multiple){
14306 var close = this.closeTriggerEl();
14308 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14309 close.on('click', this.removeBtnClick, this, close);
14313 * fix the bug in Safari iOS8
14315 this.inputEl().on("focus", function(e){
14316 document.activeElement.blur();
14324 renderTouchView : function()
14326 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14327 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14329 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14330 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14332 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14333 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14334 this.touchViewBodyEl.setStyle('overflow', 'auto');
14336 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14337 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14339 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14340 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14344 showTouchView : function()
14350 this.touchViewHeaderEl.hide();
14352 if(this.modalTitle.length){
14353 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14354 this.touchViewHeaderEl.show();
14357 this.touchViewEl.show();
14359 this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
14360 this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14361 Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14363 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14365 if(this.modalTitle.length){
14366 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14369 this.touchViewBodyEl.setHeight(bodyHeight);
14373 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14375 this.touchViewEl.addClass('in');
14378 this.doTouchViewQuery();
14382 hideTouchView : function()
14384 this.touchViewEl.removeClass('in');
14388 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14390 this.touchViewEl.setStyle('display', 'none');
14395 setTouchViewValue : function()
14402 Roo.each(this.tickItems, function(o){
14407 this.hideTouchView();
14410 doTouchViewQuery : function()
14419 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
14423 if(!this.alwaysQuery || this.mode == 'local'){
14424 this.onTouchViewLoad();
14431 onTouchViewBeforeLoad : function(combo,opts)
14437 onTouchViewLoad : function()
14439 if(this.store.getCount() < 1){
14440 this.onTouchViewEmptyResults();
14444 this.clearTouchView();
14446 var rawValue = this.getRawValue();
14448 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
14450 this.tickItems = [];
14452 this.store.data.each(function(d, rowIndex){
14453 var row = this.touchViewListGroup.createChild(template);
14455 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
14456 row.addClass(d.data.cls);
14459 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14462 html : d.data[this.displayField]
14465 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
14466 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
14469 row.removeClass('selected');
14470 if(!this.multiple && this.valueField &&
14471 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
14474 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14475 row.addClass('selected');
14478 if(this.multiple && this.valueField &&
14479 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
14483 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14484 this.tickItems.push(d.data);
14487 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
14491 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
14493 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14495 if(this.modalTitle.length){
14496 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14499 var listHeight = this.touchViewListGroup.getHeight();
14503 if(firstChecked && listHeight > bodyHeight){
14504 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
14509 onTouchViewLoadException : function()
14511 this.hideTouchView();
14514 onTouchViewEmptyResults : function()
14516 this.clearTouchView();
14518 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
14520 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
14524 clearTouchView : function()
14526 this.touchViewListGroup.dom.innerHTML = '';
14529 onTouchViewClick : function(e, el, o)
14531 e.preventDefault();
14534 var rowIndex = o.rowIndex;
14536 var r = this.store.getAt(rowIndex);
14538 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
14540 if(!this.multiple){
14541 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
14542 c.dom.removeAttribute('checked');
14545 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14547 this.setFromData(r.data);
14549 var close = this.closeTriggerEl();
14555 this.hideTouchView();
14557 this.fireEvent('select', this, r, rowIndex);
14562 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
14563 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
14564 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
14568 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14569 this.addItem(r.data);
14570 this.tickItems.push(r.data);
14574 getAutoCreateNativeIOS : function()
14577 cls: 'form-group' //input-group,
14582 cls : 'roo-ios-select'
14586 combobox.name = this.name;
14589 if (this.disabled) {
14590 combobox.disabled = true;
14593 var settings = this;
14595 ['xs','sm','md','lg'].map(function(size){
14596 if (settings[size]) {
14597 cfg.cls += ' col-' + size + '-' + settings[size];
14607 initIOSView : function()
14609 this.store.on('load', this.onIOSViewLoad, this);
14614 onIOSViewLoad : function()
14616 if(this.store.getCount() < 1){
14620 this.clearIOSView();
14622 if(this.allowBlank) {
14624 var default_text = '-- SELECT --';
14626 var opt = this.inputEl().createChild({
14629 html : default_text
14633 o[this.valueField] = 0;
14634 o[this.displayField] = default_text;
14636 this.ios_options.push({
14643 this.store.data.each(function(d, rowIndex){
14647 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14648 html = d.data[this.displayField];
14653 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
14654 value = d.data[this.valueField];
14663 if(this.value == d.data[this.valueField]){
14664 option['selected'] = true;
14667 var opt = this.inputEl().createChild(option);
14669 this.ios_options.push({
14676 this.inputEl().on('change', function(){
14677 this.fireEvent('select', this);
14682 clearIOSView: function()
14684 this.inputEl().dom.innerHTML = '';
14686 this.ios_options = [];
14689 setIOSValue: function(v)
14693 if(!this.ios_options){
14697 Roo.each(this.ios_options, function(opts){
14699 opts.el.dom.removeAttribute('selected');
14701 if(opts.data[this.valueField] != v){
14705 opts.el.dom.setAttribute('selected', true);
14711 * @cfg {Boolean} grow
14715 * @cfg {Number} growMin
14719 * @cfg {Number} growMax
14728 Roo.apply(Roo.bootstrap.ComboBox, {
14732 cls: 'modal-header',
14754 cls: 'list-group-item',
14758 cls: 'roo-combobox-list-group-item-value'
14762 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
14776 listItemCheckbox : {
14778 cls: 'list-group-item',
14782 cls: 'roo-combobox-list-group-item-value'
14786 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
14802 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
14807 cls: 'modal-footer',
14815 cls: 'col-xs-6 text-left',
14818 cls: 'btn btn-danger roo-touch-view-cancel',
14824 cls: 'col-xs-6 text-right',
14827 cls: 'btn btn-success roo-touch-view-ok',
14838 Roo.apply(Roo.bootstrap.ComboBox, {
14840 touchViewTemplate : {
14842 cls: 'modal fade roo-combobox-touch-view',
14846 cls: 'modal-dialog',
14847 style : 'position:fixed', // we have to fix position....
14851 cls: 'modal-content',
14853 Roo.bootstrap.ComboBox.header,
14854 Roo.bootstrap.ComboBox.body,
14855 Roo.bootstrap.ComboBox.footer
14864 * Ext JS Library 1.1.1
14865 * Copyright(c) 2006-2007, Ext JS, LLC.
14867 * Originally Released Under LGPL - original licence link has changed is not relivant.
14870 * <script type="text/javascript">
14875 * @extends Roo.util.Observable
14876 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
14877 * This class also supports single and multi selection modes. <br>
14878 * Create a data model bound view:
14880 var store = new Roo.data.Store(...);
14882 var view = new Roo.View({
14884 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
14886 singleSelect: true,
14887 selectedClass: "ydataview-selected",
14891 // listen for node click?
14892 view.on("click", function(vw, index, node, e){
14893 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14897 dataModel.load("foobar.xml");
14899 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14901 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14902 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14904 * Note: old style constructor is still suported (container, template, config)
14907 * Create a new View
14908 * @param {Object} config The config object
14911 Roo.View = function(config, depreciated_tpl, depreciated_config){
14913 this.parent = false;
14915 if (typeof(depreciated_tpl) == 'undefined') {
14916 // new way.. - universal constructor.
14917 Roo.apply(this, config);
14918 this.el = Roo.get(this.el);
14921 this.el = Roo.get(config);
14922 this.tpl = depreciated_tpl;
14923 Roo.apply(this, depreciated_config);
14925 this.wrapEl = this.el.wrap().wrap();
14926 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14929 if(typeof(this.tpl) == "string"){
14930 this.tpl = new Roo.Template(this.tpl);
14932 // support xtype ctors..
14933 this.tpl = new Roo.factory(this.tpl, Roo);
14937 this.tpl.compile();
14942 * @event beforeclick
14943 * Fires before a click is processed. Returns false to cancel the default action.
14944 * @param {Roo.View} this
14945 * @param {Number} index The index of the target node
14946 * @param {HTMLElement} node The target node
14947 * @param {Roo.EventObject} e The raw event object
14949 "beforeclick" : true,
14952 * Fires when a template node is clicked.
14953 * @param {Roo.View} this
14954 * @param {Number} index The index of the target node
14955 * @param {HTMLElement} node The target node
14956 * @param {Roo.EventObject} e The raw event object
14961 * Fires when a template node is double clicked.
14962 * @param {Roo.View} this
14963 * @param {Number} index The index of the target node
14964 * @param {HTMLElement} node The target node
14965 * @param {Roo.EventObject} e The raw event object
14969 * @event contextmenu
14970 * Fires when a template node is right clicked.
14971 * @param {Roo.View} this
14972 * @param {Number} index The index of the target node
14973 * @param {HTMLElement} node The target node
14974 * @param {Roo.EventObject} e The raw event object
14976 "contextmenu" : true,
14978 * @event selectionchange
14979 * Fires when the selected nodes change.
14980 * @param {Roo.View} this
14981 * @param {Array} selections Array of the selected nodes
14983 "selectionchange" : true,
14986 * @event beforeselect
14987 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
14988 * @param {Roo.View} this
14989 * @param {HTMLElement} node The node to be selected
14990 * @param {Array} selections Array of currently selected nodes
14992 "beforeselect" : true,
14994 * @event preparedata
14995 * Fires on every row to render, to allow you to change the data.
14996 * @param {Roo.View} this
14997 * @param {Object} data to be rendered (change this)
14999 "preparedata" : true
15007 "click": this.onClick,
15008 "dblclick": this.onDblClick,
15009 "contextmenu": this.onContextMenu,
15013 this.selections = [];
15015 this.cmp = new Roo.CompositeElementLite([]);
15017 this.store = Roo.factory(this.store, Roo.data);
15018 this.setStore(this.store, true);
15021 if ( this.footer && this.footer.xtype) {
15023 var fctr = this.wrapEl.appendChild(document.createElement("div"));
15025 this.footer.dataSource = this.store;
15026 this.footer.container = fctr;
15027 this.footer = Roo.factory(this.footer, Roo);
15028 fctr.insertFirst(this.el);
15030 // this is a bit insane - as the paging toolbar seems to detach the el..
15031 // dom.parentNode.parentNode.parentNode
15032 // they get detached?
15036 Roo.View.superclass.constructor.call(this);
15041 Roo.extend(Roo.View, Roo.util.Observable, {
15044 * @cfg {Roo.data.Store} store Data store to load data from.
15049 * @cfg {String|Roo.Element} el The container element.
15054 * @cfg {String|Roo.Template} tpl The template used by this View
15058 * @cfg {String} dataName the named area of the template to use as the data area
15059 * Works with domtemplates roo-name="name"
15063 * @cfg {String} selectedClass The css class to add to selected nodes
15065 selectedClass : "x-view-selected",
15067 * @cfg {String} emptyText The empty text to show when nothing is loaded.
15072 * @cfg {String} text to display on mask (default Loading)
15076 * @cfg {Boolean} multiSelect Allow multiple selection
15078 multiSelect : false,
15080 * @cfg {Boolean} singleSelect Allow single selection
15082 singleSelect: false,
15085 * @cfg {Boolean} toggleSelect - selecting
15087 toggleSelect : false,
15090 * @cfg {Boolean} tickable - selecting
15095 * Returns the element this view is bound to.
15096 * @return {Roo.Element}
15098 getEl : function(){
15099 return this.wrapEl;
15105 * Refreshes the view. - called by datachanged on the store. - do not call directly.
15107 refresh : function(){
15108 //Roo.log('refresh');
15111 // if we are using something like 'domtemplate', then
15112 // the what gets used is:
15113 // t.applySubtemplate(NAME, data, wrapping data..)
15114 // the outer template then get' applied with
15115 // the store 'extra data'
15116 // and the body get's added to the
15117 // roo-name="data" node?
15118 // <span class='roo-tpl-{name}'></span> ?????
15122 this.clearSelections();
15123 this.el.update("");
15125 var records = this.store.getRange();
15126 if(records.length < 1) {
15128 // is this valid?? = should it render a template??
15130 this.el.update(this.emptyText);
15134 if (this.dataName) {
15135 this.el.update(t.apply(this.store.meta)); //????
15136 el = this.el.child('.roo-tpl-' + this.dataName);
15139 for(var i = 0, len = records.length; i < len; i++){
15140 var data = this.prepareData(records[i].data, i, records[i]);
15141 this.fireEvent("preparedata", this, data, i, records[i]);
15143 var d = Roo.apply({}, data);
15146 Roo.apply(d, {'roo-id' : Roo.id()});
15150 Roo.each(this.parent.item, function(item){
15151 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15154 Roo.apply(d, {'roo-data-checked' : 'checked'});
15158 html[html.length] = Roo.util.Format.trim(
15160 t.applySubtemplate(this.dataName, d, this.store.meta) :
15167 el.update(html.join(""));
15168 this.nodes = el.dom.childNodes;
15169 this.updateIndexes(0);
15174 * Function to override to reformat the data that is sent to
15175 * the template for each node.
15176 * DEPRICATED - use the preparedata event handler.
15177 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15178 * a JSON object for an UpdateManager bound view).
15180 prepareData : function(data, index, record)
15182 this.fireEvent("preparedata", this, data, index, record);
15186 onUpdate : function(ds, record){
15187 // Roo.log('on update');
15188 this.clearSelections();
15189 var index = this.store.indexOf(record);
15190 var n = this.nodes[index];
15191 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
15192 n.parentNode.removeChild(n);
15193 this.updateIndexes(index, index);
15199 onAdd : function(ds, records, index)
15201 //Roo.log(['on Add', ds, records, index] );
15202 this.clearSelections();
15203 if(this.nodes.length == 0){
15207 var n = this.nodes[index];
15208 for(var i = 0, len = records.length; i < len; i++){
15209 var d = this.prepareData(records[i].data, i, records[i]);
15211 this.tpl.insertBefore(n, d);
15214 this.tpl.append(this.el, d);
15217 this.updateIndexes(index);
15220 onRemove : function(ds, record, index){
15221 // Roo.log('onRemove');
15222 this.clearSelections();
15223 var el = this.dataName ?
15224 this.el.child('.roo-tpl-' + this.dataName) :
15227 el.dom.removeChild(this.nodes[index]);
15228 this.updateIndexes(index);
15232 * Refresh an individual node.
15233 * @param {Number} index
15235 refreshNode : function(index){
15236 this.onUpdate(this.store, this.store.getAt(index));
15239 updateIndexes : function(startIndex, endIndex){
15240 var ns = this.nodes;
15241 startIndex = startIndex || 0;
15242 endIndex = endIndex || ns.length - 1;
15243 for(var i = startIndex; i <= endIndex; i++){
15244 ns[i].nodeIndex = i;
15249 * Changes the data store this view uses and refresh the view.
15250 * @param {Store} store
15252 setStore : function(store, initial){
15253 if(!initial && this.store){
15254 this.store.un("datachanged", this.refresh);
15255 this.store.un("add", this.onAdd);
15256 this.store.un("remove", this.onRemove);
15257 this.store.un("update", this.onUpdate);
15258 this.store.un("clear", this.refresh);
15259 this.store.un("beforeload", this.onBeforeLoad);
15260 this.store.un("load", this.onLoad);
15261 this.store.un("loadexception", this.onLoad);
15265 store.on("datachanged", this.refresh, this);
15266 store.on("add", this.onAdd, this);
15267 store.on("remove", this.onRemove, this);
15268 store.on("update", this.onUpdate, this);
15269 store.on("clear", this.refresh, this);
15270 store.on("beforeload", this.onBeforeLoad, this);
15271 store.on("load", this.onLoad, this);
15272 store.on("loadexception", this.onLoad, this);
15280 * onbeforeLoad - masks the loading area.
15283 onBeforeLoad : function(store,opts)
15285 //Roo.log('onBeforeLoad');
15287 this.el.update("");
15289 this.el.mask(this.mask ? this.mask : "Loading" );
15291 onLoad : function ()
15298 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15299 * @param {HTMLElement} node
15300 * @return {HTMLElement} The template node
15302 findItemFromChild : function(node){
15303 var el = this.dataName ?
15304 this.el.child('.roo-tpl-' + this.dataName,true) :
15307 if(!node || node.parentNode == el){
15310 var p = node.parentNode;
15311 while(p && p != el){
15312 if(p.parentNode == el){
15321 onClick : function(e){
15322 var item = this.findItemFromChild(e.getTarget());
15324 var index = this.indexOf(item);
15325 if(this.onItemClick(item, index, e) !== false){
15326 this.fireEvent("click", this, index, item, e);
15329 this.clearSelections();
15334 onContextMenu : function(e){
15335 var item = this.findItemFromChild(e.getTarget());
15337 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15342 onDblClick : function(e){
15343 var item = this.findItemFromChild(e.getTarget());
15345 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15349 onItemClick : function(item, index, e)
15351 if(this.fireEvent("beforeclick", this, index, item, e) === false){
15354 if (this.toggleSelect) {
15355 var m = this.isSelected(item) ? 'unselect' : 'select';
15358 _t[m](item, true, false);
15361 if(this.multiSelect || this.singleSelect){
15362 if(this.multiSelect && e.shiftKey && this.lastSelection){
15363 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
15365 this.select(item, this.multiSelect && e.ctrlKey);
15366 this.lastSelection = item;
15369 if(!this.tickable){
15370 e.preventDefault();
15378 * Get the number of selected nodes.
15381 getSelectionCount : function(){
15382 return this.selections.length;
15386 * Get the currently selected nodes.
15387 * @return {Array} An array of HTMLElements
15389 getSelectedNodes : function(){
15390 return this.selections;
15394 * Get the indexes of the selected nodes.
15397 getSelectedIndexes : function(){
15398 var indexes = [], s = this.selections;
15399 for(var i = 0, len = s.length; i < len; i++){
15400 indexes.push(s[i].nodeIndex);
15406 * Clear all selections
15407 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
15409 clearSelections : function(suppressEvent){
15410 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
15411 this.cmp.elements = this.selections;
15412 this.cmp.removeClass(this.selectedClass);
15413 this.selections = [];
15414 if(!suppressEvent){
15415 this.fireEvent("selectionchange", this, this.selections);
15421 * Returns true if the passed node is selected
15422 * @param {HTMLElement/Number} node The node or node index
15423 * @return {Boolean}
15425 isSelected : function(node){
15426 var s = this.selections;
15430 node = this.getNode(node);
15431 return s.indexOf(node) !== -1;
15436 * @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
15437 * @param {Boolean} keepExisting (optional) true to keep existing selections
15438 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15440 select : function(nodeInfo, keepExisting, suppressEvent){
15441 if(nodeInfo instanceof Array){
15443 this.clearSelections(true);
15445 for(var i = 0, len = nodeInfo.length; i < len; i++){
15446 this.select(nodeInfo[i], true, true);
15450 var node = this.getNode(nodeInfo);
15451 if(!node || this.isSelected(node)){
15452 return; // already selected.
15455 this.clearSelections(true);
15458 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
15459 Roo.fly(node).addClass(this.selectedClass);
15460 this.selections.push(node);
15461 if(!suppressEvent){
15462 this.fireEvent("selectionchange", this, this.selections);
15470 * @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
15471 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
15472 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15474 unselect : function(nodeInfo, keepExisting, suppressEvent)
15476 if(nodeInfo instanceof Array){
15477 Roo.each(this.selections, function(s) {
15478 this.unselect(s, nodeInfo);
15482 var node = this.getNode(nodeInfo);
15483 if(!node || !this.isSelected(node)){
15484 //Roo.log("not selected");
15485 return; // not selected.
15489 Roo.each(this.selections, function(s) {
15491 Roo.fly(node).removeClass(this.selectedClass);
15498 this.selections= ns;
15499 this.fireEvent("selectionchange", this, this.selections);
15503 * Gets a template node.
15504 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15505 * @return {HTMLElement} The node or null if it wasn't found
15507 getNode : function(nodeInfo){
15508 if(typeof nodeInfo == "string"){
15509 return document.getElementById(nodeInfo);
15510 }else if(typeof nodeInfo == "number"){
15511 return this.nodes[nodeInfo];
15517 * Gets a range template nodes.
15518 * @param {Number} startIndex
15519 * @param {Number} endIndex
15520 * @return {Array} An array of nodes
15522 getNodes : function(start, end){
15523 var ns = this.nodes;
15524 start = start || 0;
15525 end = typeof end == "undefined" ? ns.length - 1 : end;
15528 for(var i = start; i <= end; i++){
15532 for(var i = start; i >= end; i--){
15540 * Finds the index of the passed node
15541 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15542 * @return {Number} The index of the node or -1
15544 indexOf : function(node){
15545 node = this.getNode(node);
15546 if(typeof node.nodeIndex == "number"){
15547 return node.nodeIndex;
15549 var ns = this.nodes;
15550 for(var i = 0, len = ns.length; i < len; i++){
15561 * based on jquery fullcalendar
15565 Roo.bootstrap = Roo.bootstrap || {};
15567 * @class Roo.bootstrap.Calendar
15568 * @extends Roo.bootstrap.Component
15569 * Bootstrap Calendar class
15570 * @cfg {Boolean} loadMask (true|false) default false
15571 * @cfg {Object} header generate the user specific header of the calendar, default false
15574 * Create a new Container
15575 * @param {Object} config The config object
15580 Roo.bootstrap.Calendar = function(config){
15581 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
15585 * Fires when a date is selected
15586 * @param {DatePicker} this
15587 * @param {Date} date The selected date
15591 * @event monthchange
15592 * Fires when the displayed month changes
15593 * @param {DatePicker} this
15594 * @param {Date} date The selected month
15596 'monthchange': true,
15598 * @event evententer
15599 * Fires when mouse over an event
15600 * @param {Calendar} this
15601 * @param {event} Event
15603 'evententer': true,
15605 * @event eventleave
15606 * Fires when the mouse leaves an
15607 * @param {Calendar} this
15610 'eventleave': true,
15612 * @event eventclick
15613 * Fires when the mouse click an
15614 * @param {Calendar} this
15623 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
15626 * @cfg {Number} startDay
15627 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
15635 getAutoCreate : function(){
15638 var fc_button = function(name, corner, style, content ) {
15639 return Roo.apply({},{
15641 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
15643 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
15646 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
15657 style : 'width:100%',
15664 cls : 'fc-header-left',
15666 fc_button('prev', 'left', 'arrow', '‹' ),
15667 fc_button('next', 'right', 'arrow', '›' ),
15668 { tag: 'span', cls: 'fc-header-space' },
15669 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
15677 cls : 'fc-header-center',
15681 cls: 'fc-header-title',
15684 html : 'month / year'
15692 cls : 'fc-header-right',
15694 /* fc_button('month', 'left', '', 'month' ),
15695 fc_button('week', '', '', 'week' ),
15696 fc_button('day', 'right', '', 'day' )
15708 header = this.header;
15711 var cal_heads = function() {
15713 // fixme - handle this.
15715 for (var i =0; i < Date.dayNames.length; i++) {
15716 var d = Date.dayNames[i];
15719 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
15720 html : d.substring(0,3)
15724 ret[0].cls += ' fc-first';
15725 ret[6].cls += ' fc-last';
15728 var cal_cell = function(n) {
15731 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
15736 cls: 'fc-day-number',
15740 cls: 'fc-day-content',
15744 style: 'position: relative;' // height: 17px;
15756 var cal_rows = function() {
15759 for (var r = 0; r < 6; r++) {
15766 for (var i =0; i < Date.dayNames.length; i++) {
15767 var d = Date.dayNames[i];
15768 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
15771 row.cn[0].cls+=' fc-first';
15772 row.cn[0].cn[0].style = 'min-height:90px';
15773 row.cn[6].cls+=' fc-last';
15777 ret[0].cls += ' fc-first';
15778 ret[4].cls += ' fc-prev-last';
15779 ret[5].cls += ' fc-last';
15786 cls: 'fc-border-separate',
15787 style : 'width:100%',
15795 cls : 'fc-first fc-last',
15813 cls : 'fc-content',
15814 style : "position: relative;",
15817 cls : 'fc-view fc-view-month fc-grid',
15818 style : 'position: relative',
15819 unselectable : 'on',
15822 cls : 'fc-event-container',
15823 style : 'position:absolute;z-index:8;top:0;left:0;'
15841 initEvents : function()
15844 throw "can not find store for calendar";
15850 style: "text-align:center",
15854 style: "background-color:white;width:50%;margin:250 auto",
15858 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
15869 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15871 var size = this.el.select('.fc-content', true).first().getSize();
15872 this.maskEl.setSize(size.width, size.height);
15873 this.maskEl.enableDisplayMode("block");
15874 if(!this.loadMask){
15875 this.maskEl.hide();
15878 this.store = Roo.factory(this.store, Roo.data);
15879 this.store.on('load', this.onLoad, this);
15880 this.store.on('beforeload', this.onBeforeLoad, this);
15884 this.cells = this.el.select('.fc-day',true);
15885 //Roo.log(this.cells);
15886 this.textNodes = this.el.query('.fc-day-number');
15887 this.cells.addClassOnOver('fc-state-hover');
15889 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15890 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15891 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15892 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15894 this.on('monthchange', this.onMonthChange, this);
15896 this.update(new Date().clearTime());
15899 resize : function() {
15900 var sz = this.el.getSize();
15902 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15903 this.el.select('.fc-day-content div',true).setHeight(34);
15908 showPrevMonth : function(e){
15909 this.update(this.activeDate.add("mo", -1));
15911 showToday : function(e){
15912 this.update(new Date().clearTime());
15915 showNextMonth : function(e){
15916 this.update(this.activeDate.add("mo", 1));
15920 showPrevYear : function(){
15921 this.update(this.activeDate.add("y", -1));
15925 showNextYear : function(){
15926 this.update(this.activeDate.add("y", 1));
15931 update : function(date)
15933 var vd = this.activeDate;
15934 this.activeDate = date;
15935 // if(vd && this.el){
15936 // var t = date.getTime();
15937 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
15938 // Roo.log('using add remove');
15940 // this.fireEvent('monthchange', this, date);
15942 // this.cells.removeClass("fc-state-highlight");
15943 // this.cells.each(function(c){
15944 // if(c.dateValue == t){
15945 // c.addClass("fc-state-highlight");
15946 // setTimeout(function(){
15947 // try{c.dom.firstChild.focus();}catch(e){}
15957 var days = date.getDaysInMonth();
15959 var firstOfMonth = date.getFirstDateOfMonth();
15960 var startingPos = firstOfMonth.getDay()-this.startDay;
15962 if(startingPos < this.startDay){
15966 var pm = date.add(Date.MONTH, -1);
15967 var prevStart = pm.getDaysInMonth()-startingPos;
15969 this.cells = this.el.select('.fc-day',true);
15970 this.textNodes = this.el.query('.fc-day-number');
15971 this.cells.addClassOnOver('fc-state-hover');
15973 var cells = this.cells.elements;
15974 var textEls = this.textNodes;
15976 Roo.each(cells, function(cell){
15977 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
15980 days += startingPos;
15982 // convert everything to numbers so it's fast
15983 var day = 86400000;
15984 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
15987 //Roo.log(prevStart);
15989 var today = new Date().clearTime().getTime();
15990 var sel = date.clearTime().getTime();
15991 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
15992 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
15993 var ddMatch = this.disabledDatesRE;
15994 var ddText = this.disabledDatesText;
15995 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
15996 var ddaysText = this.disabledDaysText;
15997 var format = this.format;
15999 var setCellClass = function(cal, cell){
16003 //Roo.log('set Cell Class');
16005 var t = d.getTime();
16009 cell.dateValue = t;
16011 cell.className += " fc-today";
16012 cell.className += " fc-state-highlight";
16013 cell.title = cal.todayText;
16016 // disable highlight in other month..
16017 //cell.className += " fc-state-highlight";
16022 cell.className = " fc-state-disabled";
16023 cell.title = cal.minText;
16027 cell.className = " fc-state-disabled";
16028 cell.title = cal.maxText;
16032 if(ddays.indexOf(d.getDay()) != -1){
16033 cell.title = ddaysText;
16034 cell.className = " fc-state-disabled";
16037 if(ddMatch && format){
16038 var fvalue = d.dateFormat(format);
16039 if(ddMatch.test(fvalue)){
16040 cell.title = ddText.replace("%0", fvalue);
16041 cell.className = " fc-state-disabled";
16045 if (!cell.initialClassName) {
16046 cell.initialClassName = cell.dom.className;
16049 cell.dom.className = cell.initialClassName + ' ' + cell.className;
16054 for(; i < startingPos; i++) {
16055 textEls[i].innerHTML = (++prevStart);
16056 d.setDate(d.getDate()+1);
16058 cells[i].className = "fc-past fc-other-month";
16059 setCellClass(this, cells[i]);
16064 for(; i < days; i++){
16065 intDay = i - startingPos + 1;
16066 textEls[i].innerHTML = (intDay);
16067 d.setDate(d.getDate()+1);
16069 cells[i].className = ''; // "x-date-active";
16070 setCellClass(this, cells[i]);
16074 for(; i < 42; i++) {
16075 textEls[i].innerHTML = (++extraDays);
16076 d.setDate(d.getDate()+1);
16078 cells[i].className = "fc-future fc-other-month";
16079 setCellClass(this, cells[i]);
16082 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16084 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16086 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16087 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16089 if(totalRows != 6){
16090 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16091 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16094 this.fireEvent('monthchange', this, date);
16098 if(!this.internalRender){
16099 var main = this.el.dom.firstChild;
16100 var w = main.offsetWidth;
16101 this.el.setWidth(w + this.el.getBorderWidth("lr"));
16102 Roo.fly(main).setWidth(w);
16103 this.internalRender = true;
16104 // opera does not respect the auto grow header center column
16105 // then, after it gets a width opera refuses to recalculate
16106 // without a second pass
16107 if(Roo.isOpera && !this.secondPass){
16108 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16109 this.secondPass = true;
16110 this.update.defer(10, this, [date]);
16117 findCell : function(dt) {
16118 dt = dt.clearTime().getTime();
16120 this.cells.each(function(c){
16121 //Roo.log("check " +c.dateValue + '?=' + dt);
16122 if(c.dateValue == dt){
16132 findCells : function(ev) {
16133 var s = ev.start.clone().clearTime().getTime();
16135 var e= ev.end.clone().clearTime().getTime();
16138 this.cells.each(function(c){
16139 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16141 if(c.dateValue > e){
16144 if(c.dateValue < s){
16153 // findBestRow: function(cells)
16157 // for (var i =0 ; i < cells.length;i++) {
16158 // ret = Math.max(cells[i].rows || 0,ret);
16165 addItem : function(ev)
16167 // look for vertical location slot in
16168 var cells = this.findCells(ev);
16170 // ev.row = this.findBestRow(cells);
16172 // work out the location.
16176 for(var i =0; i < cells.length; i++) {
16178 cells[i].row = cells[0].row;
16181 cells[i].row = cells[i].row + 1;
16191 if (crow.start.getY() == cells[i].getY()) {
16193 crow.end = cells[i];
16210 cells[0].events.push(ev);
16212 this.calevents.push(ev);
16215 clearEvents: function() {
16217 if(!this.calevents){
16221 Roo.each(this.cells.elements, function(c){
16227 Roo.each(this.calevents, function(e) {
16228 Roo.each(e.els, function(el) {
16229 el.un('mouseenter' ,this.onEventEnter, this);
16230 el.un('mouseleave' ,this.onEventLeave, this);
16235 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16241 renderEvents: function()
16245 this.cells.each(function(c) {
16254 if(c.row != c.events.length){
16255 r = 4 - (4 - (c.row - c.events.length));
16258 c.events = ev.slice(0, r);
16259 c.more = ev.slice(r);
16261 if(c.more.length && c.more.length == 1){
16262 c.events.push(c.more.pop());
16265 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16269 this.cells.each(function(c) {
16271 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16274 for (var e = 0; e < c.events.length; e++){
16275 var ev = c.events[e];
16276 var rows = ev.rows;
16278 for(var i = 0; i < rows.length; i++) {
16280 // how many rows should it span..
16283 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16284 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16286 unselectable : "on",
16289 cls: 'fc-event-inner',
16293 // cls: 'fc-event-time',
16294 // html : cells.length > 1 ? '' : ev.time
16298 cls: 'fc-event-title',
16299 html : String.format('{0}', ev.title)
16306 cls: 'ui-resizable-handle ui-resizable-e',
16307 html : '  '
16314 cfg.cls += ' fc-event-start';
16316 if ((i+1) == rows.length) {
16317 cfg.cls += ' fc-event-end';
16320 var ctr = _this.el.select('.fc-event-container',true).first();
16321 var cg = ctr.createChild(cfg);
16323 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16324 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16326 var r = (c.more.length) ? 1 : 0;
16327 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
16328 cg.setWidth(ebox.right - sbox.x -2);
16330 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16331 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16332 cg.on('click', _this.onEventClick, _this, ev);
16343 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16344 style : 'position: absolute',
16345 unselectable : "on",
16348 cls: 'fc-event-inner',
16352 cls: 'fc-event-title',
16360 cls: 'ui-resizable-handle ui-resizable-e',
16361 html : '  '
16367 var ctr = _this.el.select('.fc-event-container',true).first();
16368 var cg = ctr.createChild(cfg);
16370 var sbox = c.select('.fc-day-content',true).first().getBox();
16371 var ebox = c.select('.fc-day-content',true).first().getBox();
16373 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
16374 cg.setWidth(ebox.right - sbox.x -2);
16376 cg.on('click', _this.onMoreEventClick, _this, c.more);
16386 onEventEnter: function (e, el,event,d) {
16387 this.fireEvent('evententer', this, el, event);
16390 onEventLeave: function (e, el,event,d) {
16391 this.fireEvent('eventleave', this, el, event);
16394 onEventClick: function (e, el,event,d) {
16395 this.fireEvent('eventclick', this, el, event);
16398 onMonthChange: function () {
16402 onMoreEventClick: function(e, el, more)
16406 this.calpopover.placement = 'right';
16407 this.calpopover.setTitle('More');
16409 this.calpopover.setContent('');
16411 var ctr = this.calpopover.el.select('.popover-content', true).first();
16413 Roo.each(more, function(m){
16415 cls : 'fc-event-hori fc-event-draggable',
16418 var cg = ctr.createChild(cfg);
16420 cg.on('click', _this.onEventClick, _this, m);
16423 this.calpopover.show(el);
16428 onLoad: function ()
16430 this.calevents = [];
16433 if(this.store.getCount() > 0){
16434 this.store.data.each(function(d){
16437 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
16438 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
16439 time : d.data.start_time,
16440 title : d.data.title,
16441 description : d.data.description,
16442 venue : d.data.venue
16447 this.renderEvents();
16449 if(this.calevents.length && this.loadMask){
16450 this.maskEl.hide();
16454 onBeforeLoad: function()
16456 this.clearEvents();
16458 this.maskEl.show();
16472 * @class Roo.bootstrap.Popover
16473 * @extends Roo.bootstrap.Component
16474 * Bootstrap Popover class
16475 * @cfg {String} html contents of the popover (or false to use children..)
16476 * @cfg {String} title of popover (or false to hide)
16477 * @cfg {String} placement how it is placed
16478 * @cfg {String} trigger click || hover (or false to trigger manually)
16479 * @cfg {String} over what (parent or false to trigger manually.)
16480 * @cfg {Number} delay - delay before showing
16483 * Create a new Popover
16484 * @param {Object} config The config object
16487 Roo.bootstrap.Popover = function(config){
16488 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
16494 * After the popover show
16496 * @param {Roo.bootstrap.Popover} this
16501 * After the popover hide
16503 * @param {Roo.bootstrap.Popover} this
16509 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
16511 title: 'Fill in a title',
16514 placement : 'right',
16515 trigger : 'hover', // hover
16521 can_build_overlaid : false,
16523 getChildContainer : function()
16525 return this.el.select('.popover-content',true).first();
16528 getAutoCreate : function(){
16531 cls : 'popover roo-dynamic',
16532 style: 'display:block',
16538 cls : 'popover-inner',
16542 cls: 'popover-title',
16546 cls : 'popover-content',
16557 setTitle: function(str)
16560 this.el.select('.popover-title',true).first().dom.innerHTML = str;
16562 setContent: function(str)
16565 this.el.select('.popover-content',true).first().dom.innerHTML = str;
16567 // as it get's added to the bottom of the page.
16568 onRender : function(ct, position)
16570 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
16572 var cfg = Roo.apply({}, this.getAutoCreate());
16576 cfg.cls += ' ' + this.cls;
16579 cfg.style = this.style;
16581 //Roo.log("adding to ");
16582 this.el = Roo.get(document.body).createChild(cfg, position);
16583 // Roo.log(this.el);
16588 initEvents : function()
16590 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
16591 this.el.enableDisplayMode('block');
16593 if (this.over === false) {
16596 if (this.triggers === false) {
16599 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16600 var triggers = this.trigger ? this.trigger.split(' ') : [];
16601 Roo.each(triggers, function(trigger) {
16603 if (trigger == 'click') {
16604 on_el.on('click', this.toggle, this);
16605 } else if (trigger != 'manual') {
16606 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
16607 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
16609 on_el.on(eventIn ,this.enter, this);
16610 on_el.on(eventOut, this.leave, this);
16621 toggle : function () {
16622 this.hoverState == 'in' ? this.leave() : this.enter();
16625 enter : function () {
16627 clearTimeout(this.timeout);
16629 this.hoverState = 'in';
16631 if (!this.delay || !this.delay.show) {
16636 this.timeout = setTimeout(function () {
16637 if (_t.hoverState == 'in') {
16640 }, this.delay.show)
16643 leave : function() {
16644 clearTimeout(this.timeout);
16646 this.hoverState = 'out';
16648 if (!this.delay || !this.delay.hide) {
16653 this.timeout = setTimeout(function () {
16654 if (_t.hoverState == 'out') {
16657 }, this.delay.hide)
16660 show : function (on_el)
16663 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16667 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
16668 if (this.html !== false) {
16669 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
16671 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
16672 if (!this.title.length) {
16673 this.el.select('.popover-title',true).hide();
16676 var placement = typeof this.placement == 'function' ?
16677 this.placement.call(this, this.el, on_el) :
16680 var autoToken = /\s?auto?\s?/i;
16681 var autoPlace = autoToken.test(placement);
16683 placement = placement.replace(autoToken, '') || 'top';
16687 //this.el.setXY([0,0]);
16689 this.el.dom.style.display='block';
16690 this.el.addClass(placement);
16692 //this.el.appendTo(on_el);
16694 var p = this.getPosition();
16695 var box = this.el.getBox();
16700 var align = Roo.bootstrap.Popover.alignment[placement];
16701 this.el.alignTo(on_el, align[0],align[1]);
16702 //var arrow = this.el.select('.arrow',true).first();
16703 //arrow.set(align[2],
16705 this.el.addClass('in');
16708 if (this.el.hasClass('fade')) {
16712 this.hoverState = 'in';
16714 this.fireEvent('show', this);
16719 this.el.setXY([0,0]);
16720 this.el.removeClass('in');
16722 this.hoverState = null;
16724 this.fireEvent('hide', this);
16729 Roo.bootstrap.Popover.alignment = {
16730 'left' : ['r-l', [-10,0], 'right'],
16731 'right' : ['l-r', [10,0], 'left'],
16732 'bottom' : ['t-b', [0,10], 'top'],
16733 'top' : [ 'b-t', [0,-10], 'bottom']
16744 * @class Roo.bootstrap.Progress
16745 * @extends Roo.bootstrap.Component
16746 * Bootstrap Progress class
16747 * @cfg {Boolean} striped striped of the progress bar
16748 * @cfg {Boolean} active animated of the progress bar
16752 * Create a new Progress
16753 * @param {Object} config The config object
16756 Roo.bootstrap.Progress = function(config){
16757 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
16760 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
16765 getAutoCreate : function(){
16773 cfg.cls += ' progress-striped';
16777 cfg.cls += ' active';
16796 * @class Roo.bootstrap.ProgressBar
16797 * @extends Roo.bootstrap.Component
16798 * Bootstrap ProgressBar class
16799 * @cfg {Number} aria_valuenow aria-value now
16800 * @cfg {Number} aria_valuemin aria-value min
16801 * @cfg {Number} aria_valuemax aria-value max
16802 * @cfg {String} label label for the progress bar
16803 * @cfg {String} panel (success | info | warning | danger )
16804 * @cfg {String} role role of the progress bar
16805 * @cfg {String} sr_only text
16809 * Create a new ProgressBar
16810 * @param {Object} config The config object
16813 Roo.bootstrap.ProgressBar = function(config){
16814 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
16817 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
16821 aria_valuemax : 100,
16827 getAutoCreate : function()
16832 cls: 'progress-bar',
16833 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
16845 cfg.role = this.role;
16848 if(this.aria_valuenow){
16849 cfg['aria-valuenow'] = this.aria_valuenow;
16852 if(this.aria_valuemin){
16853 cfg['aria-valuemin'] = this.aria_valuemin;
16856 if(this.aria_valuemax){
16857 cfg['aria-valuemax'] = this.aria_valuemax;
16860 if(this.label && !this.sr_only){
16861 cfg.html = this.label;
16865 cfg.cls += ' progress-bar-' + this.panel;
16871 update : function(aria_valuenow)
16873 this.aria_valuenow = aria_valuenow;
16875 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
16890 * @class Roo.bootstrap.TabGroup
16891 * @extends Roo.bootstrap.Column
16892 * Bootstrap Column class
16893 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
16894 * @cfg {Boolean} carousel true to make the group behave like a carousel
16895 * @cfg {Boolean} bullets show bullets for the panels
16896 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
16897 * @cfg {Number} timer auto slide timer .. default 0 millisecond
16898 * @cfg {Boolean} showarrow (true|false) show arrow default true
16901 * Create a new TabGroup
16902 * @param {Object} config The config object
16905 Roo.bootstrap.TabGroup = function(config){
16906 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16908 this.navId = Roo.id();
16911 Roo.bootstrap.TabGroup.register(this);
16915 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
16918 transition : false,
16923 slideOnTouch : false,
16926 getAutoCreate : function()
16928 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16930 cfg.cls += ' tab-content';
16932 if (this.carousel) {
16933 cfg.cls += ' carousel slide';
16936 cls : 'carousel-inner',
16940 if(this.bullets && !Roo.isTouch){
16943 cls : 'carousel-bullets',
16947 if(this.bullets_cls){
16948 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
16955 cfg.cn[0].cn.push(bullets);
16958 if(this.showarrow){
16959 cfg.cn[0].cn.push({
16961 class : 'carousel-arrow',
16965 class : 'carousel-prev',
16969 class : 'fa fa-chevron-left'
16975 class : 'carousel-next',
16979 class : 'fa fa-chevron-right'
16992 initEvents: function()
16994 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
16995 // this.el.on("touchstart", this.onTouchStart, this);
16998 if(this.autoslide){
17001 this.slideFn = window.setInterval(function() {
17002 _this.showPanelNext();
17006 if(this.showarrow){
17007 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17008 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17014 // onTouchStart : function(e, el, o)
17016 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17020 // this.showPanelNext();
17024 getChildContainer : function()
17026 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17030 * register a Navigation item
17031 * @param {Roo.bootstrap.NavItem} the navitem to add
17033 register : function(item)
17035 this.tabs.push( item);
17036 item.navId = this.navId; // not really needed..
17041 getActivePanel : function()
17044 Roo.each(this.tabs, function(t) {
17054 getPanelByName : function(n)
17057 Roo.each(this.tabs, function(t) {
17058 if (t.tabId == n) {
17066 indexOfPanel : function(p)
17069 Roo.each(this.tabs, function(t,i) {
17070 if (t.tabId == p.tabId) {
17079 * show a specific panel
17080 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17081 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17083 showPanel : function (pan)
17085 if(this.transition || typeof(pan) == 'undefined'){
17086 Roo.log("waiting for the transitionend");
17090 if (typeof(pan) == 'number') {
17091 pan = this.tabs[pan];
17094 if (typeof(pan) == 'string') {
17095 pan = this.getPanelByName(pan);
17098 var cur = this.getActivePanel();
17101 Roo.log('pan or acitve pan is undefined');
17105 if (pan.tabId == this.getActivePanel().tabId) {
17109 if (false === cur.fireEvent('beforedeactivate')) {
17113 if(this.bullets > 0 && !Roo.isTouch){
17114 this.setActiveBullet(this.indexOfPanel(pan));
17117 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17119 this.transition = true;
17120 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
17121 var lr = dir == 'next' ? 'left' : 'right';
17122 pan.el.addClass(dir); // or prev
17123 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17124 cur.el.addClass(lr); // or right
17125 pan.el.addClass(lr);
17128 cur.el.on('transitionend', function() {
17129 Roo.log("trans end?");
17131 pan.el.removeClass([lr,dir]);
17132 pan.setActive(true);
17134 cur.el.removeClass([lr]);
17135 cur.setActive(false);
17137 _this.transition = false;
17139 }, this, { single: true } );
17144 cur.setActive(false);
17145 pan.setActive(true);
17150 showPanelNext : function()
17152 var i = this.indexOfPanel(this.getActivePanel());
17154 if (i >= this.tabs.length - 1 && !this.autoslide) {
17158 if (i >= this.tabs.length - 1 && this.autoslide) {
17162 this.showPanel(this.tabs[i+1]);
17165 showPanelPrev : function()
17167 var i = this.indexOfPanel(this.getActivePanel());
17169 if (i < 1 && !this.autoslide) {
17173 if (i < 1 && this.autoslide) {
17174 i = this.tabs.length;
17177 this.showPanel(this.tabs[i-1]);
17181 addBullet: function()
17183 if(!this.bullets || Roo.isTouch){
17186 var ctr = this.el.select('.carousel-bullets',true).first();
17187 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
17188 var bullet = ctr.createChild({
17189 cls : 'bullet bullet-' + i
17190 },ctr.dom.lastChild);
17195 bullet.on('click', (function(e, el, o, ii, t){
17197 e.preventDefault();
17199 this.showPanel(ii);
17201 if(this.autoslide && this.slideFn){
17202 clearInterval(this.slideFn);
17203 this.slideFn = window.setInterval(function() {
17204 _this.showPanelNext();
17208 }).createDelegate(this, [i, bullet], true));
17213 setActiveBullet : function(i)
17219 Roo.each(this.el.select('.bullet', true).elements, function(el){
17220 el.removeClass('selected');
17223 var bullet = this.el.select('.bullet-' + i, true).first();
17229 bullet.addClass('selected');
17240 Roo.apply(Roo.bootstrap.TabGroup, {
17244 * register a Navigation Group
17245 * @param {Roo.bootstrap.NavGroup} the navgroup to add
17247 register : function(navgrp)
17249 this.groups[navgrp.navId] = navgrp;
17253 * fetch a Navigation Group based on the navigation ID
17254 * if one does not exist , it will get created.
17255 * @param {string} the navgroup to add
17256 * @returns {Roo.bootstrap.NavGroup} the navgroup
17258 get: function(navId) {
17259 if (typeof(this.groups[navId]) == 'undefined') {
17260 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17262 return this.groups[navId] ;
17277 * @class Roo.bootstrap.TabPanel
17278 * @extends Roo.bootstrap.Component
17279 * Bootstrap TabPanel class
17280 * @cfg {Boolean} active panel active
17281 * @cfg {String} html panel content
17282 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17283 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17284 * @cfg {String} href click to link..
17288 * Create a new TabPanel
17289 * @param {Object} config The config object
17292 Roo.bootstrap.TabPanel = function(config){
17293 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17297 * Fires when the active status changes
17298 * @param {Roo.bootstrap.TabPanel} this
17299 * @param {Boolean} state the new state
17304 * @event beforedeactivate
17305 * Fires before a tab is de-activated - can be used to do validation on a form.
17306 * @param {Roo.bootstrap.TabPanel} this
17307 * @return {Boolean} false if there is an error
17310 'beforedeactivate': true
17313 this.tabId = this.tabId || Roo.id();
17317 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
17325 getAutoCreate : function(){
17328 // item is needed for carousel - not sure if it has any effect otherwise
17329 cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17330 html: this.html || ''
17334 cfg.cls += ' active';
17338 cfg.tabId = this.tabId;
17345 initEvents: function()
17347 var p = this.parent();
17349 this.navId = this.navId || p.navId;
17351 if (typeof(this.navId) != 'undefined') {
17352 // not really needed.. but just in case.. parent should be a NavGroup.
17353 var tg = Roo.bootstrap.TabGroup.get(this.navId);
17357 var i = tg.tabs.length - 1;
17359 if(this.active && tg.bullets > 0 && i < tg.bullets){
17360 tg.setActiveBullet(i);
17364 this.el.on('click', this.onClick, this);
17367 this.el.on("touchstart", this.onTouchStart, this);
17368 this.el.on("touchmove", this.onTouchMove, this);
17369 this.el.on("touchend", this.onTouchEnd, this);
17374 onRender : function(ct, position)
17376 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
17379 setActive : function(state)
17381 Roo.log("panel - set active " + this.tabId + "=" + state);
17383 this.active = state;
17385 this.el.removeClass('active');
17387 } else if (!this.el.hasClass('active')) {
17388 this.el.addClass('active');
17391 this.fireEvent('changed', this, state);
17394 onClick : function(e)
17396 e.preventDefault();
17398 if(!this.href.length){
17402 window.location.href = this.href;
17411 onTouchStart : function(e)
17413 this.swiping = false;
17415 this.startX = e.browserEvent.touches[0].clientX;
17416 this.startY = e.browserEvent.touches[0].clientY;
17419 onTouchMove : function(e)
17421 this.swiping = true;
17423 this.endX = e.browserEvent.touches[0].clientX;
17424 this.endY = e.browserEvent.touches[0].clientY;
17427 onTouchEnd : function(e)
17434 var tabGroup = this.parent();
17436 if(this.endX > this.startX){ // swiping right
17437 tabGroup.showPanelPrev();
17441 if(this.startX > this.endX){ // swiping left
17442 tabGroup.showPanelNext();
17461 * @class Roo.bootstrap.DateField
17462 * @extends Roo.bootstrap.Input
17463 * Bootstrap DateField class
17464 * @cfg {Number} weekStart default 0
17465 * @cfg {String} viewMode default empty, (months|years)
17466 * @cfg {String} minViewMode default empty, (months|years)
17467 * @cfg {Number} startDate default -Infinity
17468 * @cfg {Number} endDate default Infinity
17469 * @cfg {Boolean} todayHighlight default false
17470 * @cfg {Boolean} todayBtn default false
17471 * @cfg {Boolean} calendarWeeks default false
17472 * @cfg {Object} daysOfWeekDisabled default empty
17473 * @cfg {Boolean} singleMode default false (true | false)
17475 * @cfg {Boolean} keyboardNavigation default true
17476 * @cfg {String} language default en
17479 * Create a new DateField
17480 * @param {Object} config The config object
17483 Roo.bootstrap.DateField = function(config){
17484 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
17488 * Fires when this field show.
17489 * @param {Roo.bootstrap.DateField} this
17490 * @param {Mixed} date The date value
17495 * Fires when this field hide.
17496 * @param {Roo.bootstrap.DateField} this
17497 * @param {Mixed} date The date value
17502 * Fires when select a date.
17503 * @param {Roo.bootstrap.DateField} this
17504 * @param {Mixed} date The date value
17508 * @event beforeselect
17509 * Fires when before select a date.
17510 * @param {Roo.bootstrap.DateField} this
17511 * @param {Mixed} date The date value
17513 beforeselect : true
17517 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
17520 * @cfg {String} format
17521 * The default date format string which can be overriden for localization support. The format must be
17522 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17526 * @cfg {String} altFormats
17527 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17528 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17530 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17538 todayHighlight : false,
17544 keyboardNavigation: true,
17546 calendarWeeks: false,
17548 startDate: -Infinity,
17552 daysOfWeekDisabled: [],
17556 singleMode : false,
17558 UTCDate: function()
17560 return new Date(Date.UTC.apply(Date, arguments));
17563 UTCToday: function()
17565 var today = new Date();
17566 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
17569 getDate: function() {
17570 var d = this.getUTCDate();
17571 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
17574 getUTCDate: function() {
17578 setDate: function(d) {
17579 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
17582 setUTCDate: function(d) {
17584 this.setValue(this.formatDate(this.date));
17587 onRender: function(ct, position)
17590 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
17592 this.language = this.language || 'en';
17593 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
17594 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
17596 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
17597 this.format = this.format || 'm/d/y';
17598 this.isInline = false;
17599 this.isInput = true;
17600 this.component = this.el.select('.add-on', true).first() || false;
17601 this.component = (this.component && this.component.length === 0) ? false : this.component;
17602 this.hasInput = this.component && this.inputEl().length;
17604 if (typeof(this.minViewMode === 'string')) {
17605 switch (this.minViewMode) {
17607 this.minViewMode = 1;
17610 this.minViewMode = 2;
17613 this.minViewMode = 0;
17618 if (typeof(this.viewMode === 'string')) {
17619 switch (this.viewMode) {
17632 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
17634 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
17636 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17638 this.picker().on('mousedown', this.onMousedown, this);
17639 this.picker().on('click', this.onClick, this);
17641 this.picker().addClass('datepicker-dropdown');
17643 this.startViewMode = this.viewMode;
17645 if(this.singleMode){
17646 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
17647 v.setVisibilityMode(Roo.Element.DISPLAY);
17651 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17652 v.setStyle('width', '189px');
17656 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
17657 if(!this.calendarWeeks){
17662 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17663 v.attr('colspan', function(i, val){
17664 return parseInt(val) + 1;
17669 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
17671 this.setStartDate(this.startDate);
17672 this.setEndDate(this.endDate);
17674 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
17681 if(this.isInline) {
17686 picker : function()
17688 return this.pickerEl;
17689 // return this.el.select('.datepicker', true).first();
17692 fillDow: function()
17694 var dowCnt = this.weekStart;
17703 if(this.calendarWeeks){
17711 while (dowCnt < this.weekStart + 7) {
17715 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
17719 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
17722 fillMonths: function()
17725 var months = this.picker().select('>.datepicker-months td', true).first();
17727 months.dom.innerHTML = '';
17733 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
17736 months.createChild(month);
17743 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;
17745 if (this.date < this.startDate) {
17746 this.viewDate = new Date(this.startDate);
17747 } else if (this.date > this.endDate) {
17748 this.viewDate = new Date(this.endDate);
17750 this.viewDate = new Date(this.date);
17758 var d = new Date(this.viewDate),
17759 year = d.getUTCFullYear(),
17760 month = d.getUTCMonth(),
17761 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
17762 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
17763 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
17764 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
17765 currentDate = this.date && this.date.valueOf(),
17766 today = this.UTCToday();
17768 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
17770 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17772 // this.picker.select('>tfoot th.today').
17773 // .text(dates[this.language].today)
17774 // .toggle(this.todayBtn !== false);
17776 this.updateNavArrows();
17779 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
17781 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
17783 prevMonth.setUTCDate(day);
17785 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
17787 var nextMonth = new Date(prevMonth);
17789 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
17791 nextMonth = nextMonth.valueOf();
17793 var fillMonths = false;
17795 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
17797 while(prevMonth.valueOf() < nextMonth) {
17800 if (prevMonth.getUTCDay() === this.weekStart) {
17802 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
17810 if(this.calendarWeeks){
17811 // ISO 8601: First week contains first thursday.
17812 // ISO also states week starts on Monday, but we can be more abstract here.
17814 // Start of current week: based on weekstart/current date
17815 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
17816 // Thursday of this week
17817 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
17818 // First Thursday of year, year from thursday
17819 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
17820 // Calendar week: ms between thursdays, div ms per day, div 7 days
17821 calWeek = (th - yth) / 864e5 / 7 + 1;
17823 fillMonths.cn.push({
17831 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
17833 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
17836 if (this.todayHighlight &&
17837 prevMonth.getUTCFullYear() == today.getFullYear() &&
17838 prevMonth.getUTCMonth() == today.getMonth() &&
17839 prevMonth.getUTCDate() == today.getDate()) {
17840 clsName += ' today';
17843 if (currentDate && prevMonth.valueOf() === currentDate) {
17844 clsName += ' active';
17847 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
17848 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
17849 clsName += ' disabled';
17852 fillMonths.cn.push({
17854 cls: 'day ' + clsName,
17855 html: prevMonth.getDate()
17858 prevMonth.setDate(prevMonth.getDate()+1);
17861 var currentYear = this.date && this.date.getUTCFullYear();
17862 var currentMonth = this.date && this.date.getUTCMonth();
17864 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
17866 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
17867 v.removeClass('active');
17869 if(currentYear === year && k === currentMonth){
17870 v.addClass('active');
17873 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
17874 v.addClass('disabled');
17880 year = parseInt(year/10, 10) * 10;
17882 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
17884 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
17887 for (var i = -1; i < 11; i++) {
17888 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
17890 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
17898 showMode: function(dir)
17901 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
17904 Roo.each(this.picker().select('>div',true).elements, function(v){
17905 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17908 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
17913 if(this.isInline) {
17917 this.picker().removeClass(['bottom', 'top']);
17919 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17921 * place to the top of element!
17925 this.picker().addClass('top');
17926 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17931 this.picker().addClass('bottom');
17933 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17936 parseDate : function(value)
17938 if(!value || value instanceof Date){
17941 var v = Date.parseDate(value, this.format);
17942 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
17943 v = Date.parseDate(value, 'Y-m-d');
17945 if(!v && this.altFormats){
17946 if(!this.altFormatsArray){
17947 this.altFormatsArray = this.altFormats.split("|");
17949 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17950 v = Date.parseDate(value, this.altFormatsArray[i]);
17956 formatDate : function(date, fmt)
17958 return (!date || !(date instanceof Date)) ?
17959 date : date.dateFormat(fmt || this.format);
17962 onFocus : function()
17964 Roo.bootstrap.DateField.superclass.onFocus.call(this);
17968 onBlur : function()
17970 Roo.bootstrap.DateField.superclass.onBlur.call(this);
17972 var d = this.inputEl().getValue();
17981 this.picker().show();
17985 this.fireEvent('show', this, this.date);
17990 if(this.isInline) {
17993 this.picker().hide();
17994 this.viewMode = this.startViewMode;
17997 this.fireEvent('hide', this, this.date);
18001 onMousedown: function(e)
18003 e.stopPropagation();
18004 e.preventDefault();
18009 Roo.bootstrap.DateField.superclass.keyup.call(this);
18013 setValue: function(v)
18015 if(this.fireEvent('beforeselect', this, v) !== false){
18016 var d = new Date(this.parseDate(v) ).clearTime();
18018 if(isNaN(d.getTime())){
18019 this.date = this.viewDate = '';
18020 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18024 v = this.formatDate(d);
18026 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18028 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18032 this.fireEvent('select', this, this.date);
18036 getValue: function()
18038 return this.formatDate(this.date);
18041 fireKey: function(e)
18043 if (!this.picker().isVisible()){
18044 if (e.keyCode == 27) { // allow escape to hide and re-show picker
18050 var dateChanged = false,
18052 newDate, newViewDate;
18057 e.preventDefault();
18061 if (!this.keyboardNavigation) {
18064 dir = e.keyCode == 37 ? -1 : 1;
18067 newDate = this.moveYear(this.date, dir);
18068 newViewDate = this.moveYear(this.viewDate, dir);
18069 } else if (e.shiftKey){
18070 newDate = this.moveMonth(this.date, dir);
18071 newViewDate = this.moveMonth(this.viewDate, dir);
18073 newDate = new Date(this.date);
18074 newDate.setUTCDate(this.date.getUTCDate() + dir);
18075 newViewDate = new Date(this.viewDate);
18076 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18078 if (this.dateWithinRange(newDate)){
18079 this.date = newDate;
18080 this.viewDate = newViewDate;
18081 this.setValue(this.formatDate(this.date));
18083 e.preventDefault();
18084 dateChanged = true;
18089 if (!this.keyboardNavigation) {
18092 dir = e.keyCode == 38 ? -1 : 1;
18094 newDate = this.moveYear(this.date, dir);
18095 newViewDate = this.moveYear(this.viewDate, dir);
18096 } else if (e.shiftKey){
18097 newDate = this.moveMonth(this.date, dir);
18098 newViewDate = this.moveMonth(this.viewDate, dir);
18100 newDate = new Date(this.date);
18101 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18102 newViewDate = new Date(this.viewDate);
18103 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18105 if (this.dateWithinRange(newDate)){
18106 this.date = newDate;
18107 this.viewDate = newViewDate;
18108 this.setValue(this.formatDate(this.date));
18110 e.preventDefault();
18111 dateChanged = true;
18115 this.setValue(this.formatDate(this.date));
18117 e.preventDefault();
18120 this.setValue(this.formatDate(this.date));
18134 onClick: function(e)
18136 e.stopPropagation();
18137 e.preventDefault();
18139 var target = e.getTarget();
18141 if(target.nodeName.toLowerCase() === 'i'){
18142 target = Roo.get(target).dom.parentNode;
18145 var nodeName = target.nodeName;
18146 var className = target.className;
18147 var html = target.innerHTML;
18148 //Roo.log(nodeName);
18150 switch(nodeName.toLowerCase()) {
18152 switch(className) {
18158 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18159 switch(this.viewMode){
18161 this.viewDate = this.moveMonth(this.viewDate, dir);
18165 this.viewDate = this.moveYear(this.viewDate, dir);
18171 var date = new Date();
18172 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18174 this.setValue(this.formatDate(this.date));
18181 if (className.indexOf('disabled') < 0) {
18182 this.viewDate.setUTCDate(1);
18183 if (className.indexOf('month') > -1) {
18184 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18186 var year = parseInt(html, 10) || 0;
18187 this.viewDate.setUTCFullYear(year);
18191 if(this.singleMode){
18192 this.setValue(this.formatDate(this.viewDate));
18203 //Roo.log(className);
18204 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
18205 var day = parseInt(html, 10) || 1;
18206 var year = this.viewDate.getUTCFullYear(),
18207 month = this.viewDate.getUTCMonth();
18209 if (className.indexOf('old') > -1) {
18216 } else if (className.indexOf('new') > -1) {
18224 //Roo.log([year,month,day]);
18225 this.date = this.UTCDate(year, month, day,0,0,0,0);
18226 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
18228 //Roo.log(this.formatDate(this.date));
18229 this.setValue(this.formatDate(this.date));
18236 setStartDate: function(startDate)
18238 this.startDate = startDate || -Infinity;
18239 if (this.startDate !== -Infinity) {
18240 this.startDate = this.parseDate(this.startDate);
18243 this.updateNavArrows();
18246 setEndDate: function(endDate)
18248 this.endDate = endDate || Infinity;
18249 if (this.endDate !== Infinity) {
18250 this.endDate = this.parseDate(this.endDate);
18253 this.updateNavArrows();
18256 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18258 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18259 if (typeof(this.daysOfWeekDisabled) !== 'object') {
18260 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18262 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18263 return parseInt(d, 10);
18266 this.updateNavArrows();
18269 updateNavArrows: function()
18271 if(this.singleMode){
18275 var d = new Date(this.viewDate),
18276 year = d.getUTCFullYear(),
18277 month = d.getUTCMonth();
18279 Roo.each(this.picker().select('.prev', true).elements, function(v){
18281 switch (this.viewMode) {
18284 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18290 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18297 Roo.each(this.picker().select('.next', true).elements, function(v){
18299 switch (this.viewMode) {
18302 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18308 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18316 moveMonth: function(date, dir)
18321 var new_date = new Date(date.valueOf()),
18322 day = new_date.getUTCDate(),
18323 month = new_date.getUTCMonth(),
18324 mag = Math.abs(dir),
18326 dir = dir > 0 ? 1 : -1;
18329 // If going back one month, make sure month is not current month
18330 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18332 return new_date.getUTCMonth() == month;
18334 // If going forward one month, make sure month is as expected
18335 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18337 return new_date.getUTCMonth() != new_month;
18339 new_month = month + dir;
18340 new_date.setUTCMonth(new_month);
18341 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18342 if (new_month < 0 || new_month > 11) {
18343 new_month = (new_month + 12) % 12;
18346 // For magnitudes >1, move one month at a time...
18347 for (var i=0; i<mag; i++) {
18348 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18349 new_date = this.moveMonth(new_date, dir);
18351 // ...then reset the day, keeping it in the new month
18352 new_month = new_date.getUTCMonth();
18353 new_date.setUTCDate(day);
18355 return new_month != new_date.getUTCMonth();
18358 // Common date-resetting loop -- if date is beyond end of month, make it
18361 new_date.setUTCDate(--day);
18362 new_date.setUTCMonth(new_month);
18367 moveYear: function(date, dir)
18369 return this.moveMonth(date, dir*12);
18372 dateWithinRange: function(date)
18374 return date >= this.startDate && date <= this.endDate;
18380 this.picker().remove();
18383 validateValue : function(value)
18385 if(value.length < 1) {
18386 if(this.allowBlank){
18392 if(value.length < this.minLength){
18395 if(value.length > this.maxLength){
18399 var vt = Roo.form.VTypes;
18400 if(!vt[this.vtype](value, this)){
18404 if(typeof this.validator == "function"){
18405 var msg = this.validator(value);
18411 if(this.regex && !this.regex.test(value)){
18415 if(typeof(this.parseDate(value)) == 'undefined'){
18419 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
18423 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
18433 Roo.apply(Roo.bootstrap.DateField, {
18444 html: '<i class="fa fa-arrow-left"/>'
18454 html: '<i class="fa fa-arrow-right"/>'
18496 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
18497 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
18498 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
18499 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18500 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
18513 navFnc: 'FullYear',
18518 navFnc: 'FullYear',
18523 Roo.apply(Roo.bootstrap.DateField, {
18527 cls: 'datepicker dropdown-menu roo-dynamic',
18531 cls: 'datepicker-days',
18535 cls: 'table-condensed',
18537 Roo.bootstrap.DateField.head,
18541 Roo.bootstrap.DateField.footer
18548 cls: 'datepicker-months',
18552 cls: 'table-condensed',
18554 Roo.bootstrap.DateField.head,
18555 Roo.bootstrap.DateField.content,
18556 Roo.bootstrap.DateField.footer
18563 cls: 'datepicker-years',
18567 cls: 'table-condensed',
18569 Roo.bootstrap.DateField.head,
18570 Roo.bootstrap.DateField.content,
18571 Roo.bootstrap.DateField.footer
18590 * @class Roo.bootstrap.TimeField
18591 * @extends Roo.bootstrap.Input
18592 * Bootstrap DateField class
18596 * Create a new TimeField
18597 * @param {Object} config The config object
18600 Roo.bootstrap.TimeField = function(config){
18601 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
18605 * Fires when this field show.
18606 * @param {Roo.bootstrap.DateField} thisthis
18607 * @param {Mixed} date The date value
18612 * Fires when this field hide.
18613 * @param {Roo.bootstrap.DateField} this
18614 * @param {Mixed} date The date value
18619 * Fires when select a date.
18620 * @param {Roo.bootstrap.DateField} this
18621 * @param {Mixed} date The date value
18627 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
18630 * @cfg {String} format
18631 * The default time format string which can be overriden for localization support. The format must be
18632 * valid according to {@link Date#parseDate} (defaults to 'H:i').
18636 onRender: function(ct, position)
18639 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
18641 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
18643 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18645 this.pop = this.picker().select('>.datepicker-time',true).first();
18646 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18648 this.picker().on('mousedown', this.onMousedown, this);
18649 this.picker().on('click', this.onClick, this);
18651 this.picker().addClass('datepicker-dropdown');
18656 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
18657 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
18658 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
18659 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
18660 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
18661 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
18665 fireKey: function(e){
18666 if (!this.picker().isVisible()){
18667 if (e.keyCode == 27) { // allow escape to hide and re-show picker
18673 e.preventDefault();
18681 this.onTogglePeriod();
18684 this.onIncrementMinutes();
18687 this.onDecrementMinutes();
18696 onClick: function(e) {
18697 e.stopPropagation();
18698 e.preventDefault();
18701 picker : function()
18703 return this.el.select('.datepicker', true).first();
18706 fillTime: function()
18708 var time = this.pop.select('tbody', true).first();
18710 time.dom.innerHTML = '';
18725 cls: 'hours-up glyphicon glyphicon-chevron-up'
18745 cls: 'minutes-up glyphicon glyphicon-chevron-up'
18766 cls: 'timepicker-hour',
18781 cls: 'timepicker-minute',
18796 cls: 'btn btn-primary period',
18818 cls: 'hours-down glyphicon glyphicon-chevron-down'
18838 cls: 'minutes-down glyphicon glyphicon-chevron-down'
18856 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
18863 var hours = this.time.getHours();
18864 var minutes = this.time.getMinutes();
18877 hours = hours - 12;
18881 hours = '0' + hours;
18885 minutes = '0' + minutes;
18888 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
18889 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
18890 this.pop.select('button', true).first().dom.innerHTML = period;
18896 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
18898 var cls = ['bottom'];
18900 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
18907 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
18912 this.picker().addClass(cls.join('-'));
18916 Roo.each(cls, function(c){
18918 _this.picker().setTop(_this.inputEl().getHeight());
18922 _this.picker().setTop(0 - _this.picker().getHeight());
18927 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
18931 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
18938 onFocus : function()
18940 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
18944 onBlur : function()
18946 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
18952 this.picker().show();
18957 this.fireEvent('show', this, this.date);
18962 this.picker().hide();
18965 this.fireEvent('hide', this, this.date);
18968 setTime : function()
18971 this.setValue(this.time.format(this.format));
18973 this.fireEvent('select', this, this.date);
18978 onMousedown: function(e){
18979 e.stopPropagation();
18980 e.preventDefault();
18983 onIncrementHours: function()
18985 Roo.log('onIncrementHours');
18986 this.time = this.time.add(Date.HOUR, 1);
18991 onDecrementHours: function()
18993 Roo.log('onDecrementHours');
18994 this.time = this.time.add(Date.HOUR, -1);
18998 onIncrementMinutes: function()
19000 Roo.log('onIncrementMinutes');
19001 this.time = this.time.add(Date.MINUTE, 1);
19005 onDecrementMinutes: function()
19007 Roo.log('onDecrementMinutes');
19008 this.time = this.time.add(Date.MINUTE, -1);
19012 onTogglePeriod: function()
19014 Roo.log('onTogglePeriod');
19015 this.time = this.time.add(Date.HOUR, 12);
19022 Roo.apply(Roo.bootstrap.TimeField, {
19052 cls: 'btn btn-info ok',
19064 Roo.apply(Roo.bootstrap.TimeField, {
19068 cls: 'datepicker dropdown-menu',
19072 cls: 'datepicker-time',
19076 cls: 'table-condensed',
19078 Roo.bootstrap.TimeField.content,
19079 Roo.bootstrap.TimeField.footer
19098 * @class Roo.bootstrap.MonthField
19099 * @extends Roo.bootstrap.Input
19100 * Bootstrap MonthField class
19102 * @cfg {String} language default en
19105 * Create a new MonthField
19106 * @param {Object} config The config object
19109 Roo.bootstrap.MonthField = function(config){
19110 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19115 * Fires when this field show.
19116 * @param {Roo.bootstrap.MonthField} this
19117 * @param {Mixed} date The date value
19122 * Fires when this field hide.
19123 * @param {Roo.bootstrap.MonthField} this
19124 * @param {Mixed} date The date value
19129 * Fires when select a date.
19130 * @param {Roo.bootstrap.MonthField} this
19131 * @param {String} oldvalue The old value
19132 * @param {String} newvalue The new value
19138 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
19140 onRender: function(ct, position)
19143 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19145 this.language = this.language || 'en';
19146 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19147 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19149 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19150 this.isInline = false;
19151 this.isInput = true;
19152 this.component = this.el.select('.add-on', true).first() || false;
19153 this.component = (this.component && this.component.length === 0) ? false : this.component;
19154 this.hasInput = this.component && this.inputEL().length;
19156 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19158 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19160 this.picker().on('mousedown', this.onMousedown, this);
19161 this.picker().on('click', this.onClick, this);
19163 this.picker().addClass('datepicker-dropdown');
19165 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19166 v.setStyle('width', '189px');
19173 if(this.isInline) {
19179 setValue: function(v, suppressEvent)
19181 var o = this.getValue();
19183 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
19187 if(suppressEvent !== true){
19188 this.fireEvent('select', this, o, v);
19193 getValue: function()
19198 onClick: function(e)
19200 e.stopPropagation();
19201 e.preventDefault();
19203 var target = e.getTarget();
19205 if(target.nodeName.toLowerCase() === 'i'){
19206 target = Roo.get(target).dom.parentNode;
19209 var nodeName = target.nodeName;
19210 var className = target.className;
19211 var html = target.innerHTML;
19213 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
19217 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
19219 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19225 picker : function()
19227 return this.pickerEl;
19230 fillMonths: function()
19233 var months = this.picker().select('>.datepicker-months td', true).first();
19235 months.dom.innerHTML = '';
19241 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
19244 months.createChild(month);
19253 if(typeof(this.vIndex) == 'undefined' && this.value.length){
19254 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19257 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19258 e.removeClass('active');
19260 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19261 e.addClass('active');
19268 if(this.isInline) {
19272 this.picker().removeClass(['bottom', 'top']);
19274 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19276 * place to the top of element!
19280 this.picker().addClass('top');
19281 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19286 this.picker().addClass('bottom');
19288 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19291 onFocus : function()
19293 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19297 onBlur : function()
19299 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19301 var d = this.inputEl().getValue();
19310 this.picker().show();
19311 this.picker().select('>.datepicker-months', true).first().show();
19315 this.fireEvent('show', this, this.date);
19320 if(this.isInline) {
19323 this.picker().hide();
19324 this.fireEvent('hide', this, this.date);
19328 onMousedown: function(e)
19330 e.stopPropagation();
19331 e.preventDefault();
19336 Roo.bootstrap.MonthField.superclass.keyup.call(this);
19340 fireKey: function(e)
19342 if (!this.picker().isVisible()){
19343 if (e.keyCode == 27) {// allow escape to hide and re-show picker
19354 e.preventDefault();
19358 dir = e.keyCode == 37 ? -1 : 1;
19360 this.vIndex = this.vIndex + dir;
19362 if(this.vIndex < 0){
19366 if(this.vIndex > 11){
19370 if(isNaN(this.vIndex)){
19374 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19380 dir = e.keyCode == 38 ? -1 : 1;
19382 this.vIndex = this.vIndex + dir * 4;
19384 if(this.vIndex < 0){
19388 if(this.vIndex > 11){
19392 if(isNaN(this.vIndex)){
19396 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19401 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19402 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19406 e.preventDefault();
19409 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19410 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19426 this.picker().remove();
19431 Roo.apply(Roo.bootstrap.MonthField, {
19450 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19451 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
19456 Roo.apply(Roo.bootstrap.MonthField, {
19460 cls: 'datepicker dropdown-menu roo-dynamic',
19464 cls: 'datepicker-months',
19468 cls: 'table-condensed',
19470 Roo.bootstrap.DateField.content
19490 * @class Roo.bootstrap.CheckBox
19491 * @extends Roo.bootstrap.Input
19492 * Bootstrap CheckBox class
19494 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
19495 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
19496 * @cfg {String} boxLabel The text that appears beside the checkbox
19497 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
19498 * @cfg {Boolean} checked initnal the element
19499 * @cfg {Boolean} inline inline the element (default false)
19500 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
19503 * Create a new CheckBox
19504 * @param {Object} config The config object
19507 Roo.bootstrap.CheckBox = function(config){
19508 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
19513 * Fires when the element is checked or unchecked.
19514 * @param {Roo.bootstrap.CheckBox} this This input
19515 * @param {Boolean} checked The new checked value
19522 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
19524 inputType: 'checkbox',
19532 getAutoCreate : function()
19534 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19540 cfg.cls = 'form-group ' + this.inputType; //input-group
19543 cfg.cls += ' ' + this.inputType + '-inline';
19549 type : this.inputType,
19550 value : this.inputValue,
19551 cls : 'roo-' + this.inputType, //'form-box',
19552 placeholder : this.placeholder || ''
19556 if(this.inputType != 'radio'){
19560 cls : 'roo-hidden-value',
19561 value : this.checked ? this.valueOff : this.inputValue
19566 if (this.weight) { // Validity check?
19567 cfg.cls += " " + this.inputType + "-" + this.weight;
19570 if (this.disabled) {
19571 input.disabled=true;
19575 input.checked = this.checked;
19582 input.name = this.name;
19584 if(this.inputType != 'radio'){
19585 hidden.name = this.name;
19586 input.name = '_hidden_' + this.name;
19591 input.cls += ' input-' + this.size;
19596 ['xs','sm','md','lg'].map(function(size){
19597 if (settings[size]) {
19598 cfg.cls += ' col-' + size + '-' + settings[size];
19602 var inputblock = input;
19604 if (this.before || this.after) {
19607 cls : 'input-group',
19612 inputblock.cn.push({
19614 cls : 'input-group-addon',
19619 inputblock.cn.push(input);
19621 if(this.inputType != 'radio'){
19622 inputblock.cn.push(hidden);
19626 inputblock.cn.push({
19628 cls : 'input-group-addon',
19635 if (align ==='left' && this.fieldLabel.length) {
19636 // Roo.log("left and has label");
19642 cls : 'control-label col-md-' + this.labelWidth,
19643 html : this.fieldLabel
19647 cls : "col-md-" + (12 - this.labelWidth),
19654 } else if ( this.fieldLabel.length) {
19655 // Roo.log(" label");
19659 tag: this.boxLabel ? 'span' : 'label',
19661 cls: 'control-label box-input-label',
19662 //cls : 'input-group-addon',
19663 html : this.fieldLabel
19673 // Roo.log(" no label && no align");
19674 cfg.cn = [ inputblock ] ;
19680 var boxLabelCfg = {
19682 //'for': id, // box label is handled by onclick - so no for...
19684 html: this.boxLabel
19688 boxLabelCfg.tooltip = this.tooltip;
19691 cfg.cn.push(boxLabelCfg);
19694 if(this.inputType != 'radio'){
19695 cfg.cn.push(hidden);
19703 * return the real input element.
19705 inputEl: function ()
19707 return this.el.select('input.roo-' + this.inputType,true).first();
19709 hiddenEl: function ()
19711 return this.el.select('input.roo-hidden-value',true).first();
19714 labelEl: function()
19716 return this.el.select('label.control-label',true).first();
19718 /* depricated... */
19722 return this.labelEl();
19725 boxLabelEl: function()
19727 return this.el.select('label.box-label',true).first();
19730 initEvents : function()
19732 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19734 this.inputEl().on('click', this.onClick, this);
19736 if (this.boxLabel) {
19737 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
19740 this.startValue = this.getValue();
19743 Roo.bootstrap.CheckBox.register(this);
19747 onClick : function()
19749 this.setChecked(!this.checked);
19752 setChecked : function(state,suppressEvent)
19754 this.startValue = this.getValue();
19756 if(this.inputType == 'radio'){
19758 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19759 e.dom.checked = false;
19762 this.inputEl().dom.checked = true;
19764 this.inputEl().dom.value = this.inputValue;
19766 if(suppressEvent !== true){
19767 this.fireEvent('check', this, true);
19775 this.checked = state;
19777 this.inputEl().dom.checked = state;
19780 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
19782 if(suppressEvent !== true){
19783 this.fireEvent('check', this, state);
19789 getValue : function()
19791 if(this.inputType == 'radio'){
19792 return this.getGroupValue();
19795 return this.hiddenEl().dom.value;
19799 getGroupValue : function()
19801 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
19805 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
19808 setValue : function(v,suppressEvent)
19810 if(this.inputType == 'radio'){
19811 this.setGroupValue(v, suppressEvent);
19815 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
19820 setGroupValue : function(v, suppressEvent)
19822 this.startValue = this.getValue();
19824 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19825 e.dom.checked = false;
19827 if(e.dom.value == v){
19828 e.dom.checked = true;
19832 if(suppressEvent !== true){
19833 this.fireEvent('check', this, true);
19841 validate : function()
19845 (this.inputType == 'radio' && this.validateRadio()) ||
19846 (this.inputType == 'checkbox' && this.validateCheckbox())
19852 this.markInvalid();
19856 validateRadio : function()
19858 if(this.allowBlank){
19864 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19865 if(!e.dom.checked){
19877 validateCheckbox : function()
19880 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
19883 var group = Roo.bootstrap.CheckBox.get(this.groupId);
19891 for(var i in group){
19896 r = (group[i].getValue() == group[i].inputValue) ? true : false;
19903 * Mark this field as valid
19905 markValid : function()
19907 if(this.allowBlank){
19913 this.fireEvent('valid', this);
19915 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19918 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19925 if(this.inputType == 'radio'){
19926 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19927 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19928 e.findParent('.form-group', false, true).addClass(_this.validClass);
19935 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19936 this.el.findParent('.form-group', false, true).addClass(this.validClass);
19940 var group = Roo.bootstrap.CheckBox.get(this.groupId);
19946 for(var i in group){
19947 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19948 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
19953 * Mark this field as invalid
19954 * @param {String} msg The validation message
19956 markInvalid : function(msg)
19958 if(this.allowBlank){
19964 this.fireEvent('invalid', this, msg);
19966 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19969 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19973 label.markInvalid();
19976 if(this.inputType == 'radio'){
19977 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19978 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19979 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
19986 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19987 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
19991 var group = Roo.bootstrap.CheckBox.get(this.groupId);
19997 for(var i in group){
19998 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19999 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20004 disable : function()
20006 if(this.inputType != 'radio'){
20007 Roo.bootstrap.CheckBox.superclass.disable.call(this);
20014 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20015 _this.getActionEl().addClass(this.disabledClass);
20016 e.dom.disabled = true;
20020 this.disabled = true;
20021 this.fireEvent("disable", this);
20025 enable : function()
20027 if(this.inputType != 'radio'){
20028 Roo.bootstrap.CheckBox.superclass.enable.call(this);
20035 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20036 _this.getActionEl().removeClass(this.disabledClass);
20037 e.dom.disabled = false;
20041 this.disabled = false;
20042 this.fireEvent("enable", this);
20048 Roo.apply(Roo.bootstrap.CheckBox, {
20053 * register a CheckBox Group
20054 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20056 register : function(checkbox)
20058 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20059 this.groups[checkbox.groupId] = {};
20062 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20066 this.groups[checkbox.groupId][checkbox.name] = checkbox;
20070 * fetch a CheckBox Group based on the group ID
20071 * @param {string} the group ID
20072 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20074 get: function(groupId) {
20075 if (typeof(this.groups[groupId]) == 'undefined') {
20079 return this.groups[groupId] ;
20091 *<div class="radio">
20093 <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
20094 Option one is this and that—be sure to include why it's great
20101 *<label class="radio-inline">fieldLabel</label>
20102 *<label class="radio-inline">
20103 <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
20111 * @class Roo.bootstrap.Radio
20112 * @extends Roo.bootstrap.CheckBox
20113 * Bootstrap Radio class
20116 * Create a new Radio
20117 * @param {Object} config The config object
20120 Roo.bootstrap.Radio = function(config){
20121 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
20125 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
20127 inputType: 'radio',
20131 getAutoCreate : function()
20133 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20134 align = align || 'left'; // default...
20141 tag : this.inline ? 'span' : 'div',
20142 cls : 'form-group',
20146 var inline = this.inline ? ' radio-inline' : '';
20150 // does not need for, as we wrap the input with it..
20152 cls : 'control-label box-label' + inline,
20155 var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
20159 //cls : 'control-label' + inline,
20160 html : this.fieldLabel,
20161 style : 'width:' + labelWidth + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
20167 type : this.inputType,
20168 //value : (!this.checked) ? this.valueOff : this.inputValue,
20169 value : this.inputValue,
20171 placeholder : this.placeholder || '' // ?? needed????
20174 if (this.weight) { // Validity check?
20175 input.cls += " radio-" + this.weight;
20177 if (this.disabled) {
20178 input.disabled=true;
20182 input.checked = this.checked;
20186 input.name = this.name;
20190 input.cls += ' input-' + this.size;
20193 //?? can span's inline have a width??
20196 ['xs','sm','md','lg'].map(function(size){
20197 if (settings[size]) {
20198 cfg.cls += ' col-' + size + '-' + settings[size];
20202 var inputblock = input;
20204 if (this.before || this.after) {
20207 cls : 'input-group',
20212 inputblock.cn.push({
20214 cls : 'input-group-addon',
20218 inputblock.cn.push(input);
20220 inputblock.cn.push({
20222 cls : 'input-group-addon',
20230 if (this.fieldLabel && this.fieldLabel.length) {
20231 cfg.cn.push(fieldLabel);
20234 // normal bootstrap puts the input inside the label.
20235 // however with our styled version - it has to go after the input.
20237 //lbl.cn.push(inputblock);
20241 cls: 'radio' + inline,
20248 cfg.cn.push( lblwrap);
20253 html: this.boxLabel
20262 initEvents : function()
20264 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20266 this.inputEl().on('click', this.onClick, this);
20267 if (this.boxLabel) {
20268 //Roo.log('find label');
20269 this.el.select('span.radio label span',true).first().on('click', this.onClick, this);
20274 inputEl: function ()
20276 return this.el.select('input.roo-radio',true).first();
20278 onClick : function()
20281 this.setChecked(true);
20284 setChecked : function(state,suppressEvent)
20287 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
20288 v.dom.checked = false;
20291 Roo.log(this.inputEl().dom);
20292 this.checked = state;
20293 this.inputEl().dom.checked = state;
20295 if(suppressEvent !== true){
20296 this.fireEvent('check', this, state);
20299 //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
20303 getGroupValue : function()
20306 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
20307 if(v.dom.checked == true){
20308 value = v.dom.value;
20316 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
20317 * @return {Mixed} value The field value
20319 getValue : function(){
20320 return this.getGroupValue();
20324 //<script type="text/javascript">
20327 * Based Ext JS Library 1.1.1
20328 * Copyright(c) 2006-2007, Ext JS, LLC.
20334 * @class Roo.HtmlEditorCore
20335 * @extends Roo.Component
20336 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20338 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20341 Roo.HtmlEditorCore = function(config){
20344 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20349 * @event initialize
20350 * Fires when the editor is fully initialized (including the iframe)
20351 * @param {Roo.HtmlEditorCore} this
20356 * Fires when the editor is first receives the focus. Any insertion must wait
20357 * until after this event.
20358 * @param {Roo.HtmlEditorCore} this
20362 * @event beforesync
20363 * Fires before the textarea is updated with content from the editor iframe. Return false
20364 * to cancel the sync.
20365 * @param {Roo.HtmlEditorCore} this
20366 * @param {String} html
20370 * @event beforepush
20371 * Fires before the iframe editor is updated with content from the textarea. Return false
20372 * to cancel the push.
20373 * @param {Roo.HtmlEditorCore} this
20374 * @param {String} html
20379 * Fires when the textarea is updated with content from the editor iframe.
20380 * @param {Roo.HtmlEditorCore} this
20381 * @param {String} html
20386 * Fires when the iframe editor is updated with content from the textarea.
20387 * @param {Roo.HtmlEditorCore} this
20388 * @param {String} html
20393 * @event editorevent
20394 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20395 * @param {Roo.HtmlEditorCore} this
20401 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20403 // defaults : white / black...
20404 this.applyBlacklists();
20411 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
20415 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
20421 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
20426 * @cfg {Number} height (in pixels)
20430 * @cfg {Number} width (in pixels)
20435 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20438 stylesheets: false,
20443 // private properties
20444 validationEvent : false,
20446 initialized : false,
20448 sourceEditMode : false,
20449 onFocus : Roo.emptyFn,
20451 hideMode:'offsets',
20455 // blacklist + whitelisted elements..
20462 * Protected method that will not generally be called directly. It
20463 * is called when the editor initializes the iframe with HTML contents. Override this method if you
20464 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20466 getDocMarkup : function(){
20470 // inherit styels from page...??
20471 if (this.stylesheets === false) {
20473 Roo.get(document.head).select('style').each(function(node) {
20474 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20477 Roo.get(document.head).select('link').each(function(node) {
20478 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20481 } else if (!this.stylesheets.length) {
20483 st = '<style type="text/css">' +
20484 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20490 st += '<style type="text/css">' +
20491 'IMG { cursor: pointer } ' +
20495 return '<html><head>' + st +
20496 //<style type="text/css">' +
20497 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20499 ' </head><body class="roo-htmleditor-body"></body></html>';
20503 onRender : function(ct, position)
20506 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20507 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20510 this.el.dom.style.border = '0 none';
20511 this.el.dom.setAttribute('tabIndex', -1);
20512 this.el.addClass('x-hidden hide');
20516 if(Roo.isIE){ // fix IE 1px bogus margin
20517 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20521 this.frameId = Roo.id();
20525 var iframe = this.owner.wrap.createChild({
20527 cls: 'form-control', // bootstrap..
20529 name: this.frameId,
20530 frameBorder : 'no',
20531 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
20536 this.iframe = iframe.dom;
20538 this.assignDocWin();
20540 this.doc.designMode = 'on';
20543 this.doc.write(this.getDocMarkup());
20547 var task = { // must defer to wait for browser to be ready
20549 //console.log("run task?" + this.doc.readyState);
20550 this.assignDocWin();
20551 if(this.doc.body || this.doc.readyState == 'complete'){
20553 this.doc.designMode="on";
20557 Roo.TaskMgr.stop(task);
20558 this.initEditor.defer(10, this);
20565 Roo.TaskMgr.start(task);
20570 onResize : function(w, h)
20572 Roo.log('resize: ' +w + ',' + h );
20573 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20577 if(typeof w == 'number'){
20579 this.iframe.style.width = w + 'px';
20581 if(typeof h == 'number'){
20583 this.iframe.style.height = h + 'px';
20585 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20592 * Toggles the editor between standard and source edit mode.
20593 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20595 toggleSourceEdit : function(sourceEditMode){
20597 this.sourceEditMode = sourceEditMode === true;
20599 if(this.sourceEditMode){
20601 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
20604 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20605 //this.iframe.className = '';
20608 //this.setSize(this.owner.wrap.getSize());
20609 //this.fireEvent('editmodechange', this, this.sourceEditMode);
20616 * Protected method that will not generally be called directly. If you need/want
20617 * custom HTML cleanup, this is the method you should override.
20618 * @param {String} html The HTML to be cleaned
20619 * return {String} The cleaned HTML
20621 cleanHtml : function(html){
20622 html = String(html);
20623 if(html.length > 5){
20624 if(Roo.isSafari){ // strip safari nonsense
20625 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20628 if(html == ' '){
20635 * HTML Editor -> Textarea
20636 * Protected method that will not generally be called directly. Syncs the contents
20637 * of the editor iframe with the textarea.
20639 syncValue : function(){
20640 if(this.initialized){
20641 var bd = (this.doc.body || this.doc.documentElement);
20642 //this.cleanUpPaste(); -- this is done else where and causes havoc..
20643 var html = bd.innerHTML;
20645 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20646 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20648 html = '<div style="'+m[0]+'">' + html + '</div>';
20651 html = this.cleanHtml(html);
20652 // fix up the special chars.. normaly like back quotes in word...
20653 // however we do not want to do this with chinese..
20654 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
20655 var cc = b.charCodeAt();
20657 (cc >= 0x4E00 && cc < 0xA000 ) ||
20658 (cc >= 0x3400 && cc < 0x4E00 ) ||
20659 (cc >= 0xf900 && cc < 0xfb00 )
20665 if(this.owner.fireEvent('beforesync', this, html) !== false){
20666 this.el.dom.value = html;
20667 this.owner.fireEvent('sync', this, html);
20673 * Protected method that will not generally be called directly. Pushes the value of the textarea
20674 * into the iframe editor.
20676 pushValue : function(){
20677 if(this.initialized){
20678 var v = this.el.dom.value.trim();
20680 // if(v.length < 1){
20684 if(this.owner.fireEvent('beforepush', this, v) !== false){
20685 var d = (this.doc.body || this.doc.documentElement);
20687 this.cleanUpPaste();
20688 this.el.dom.value = d.innerHTML;
20689 this.owner.fireEvent('push', this, v);
20695 deferFocus : function(){
20696 this.focus.defer(10, this);
20700 focus : function(){
20701 if(this.win && !this.sourceEditMode){
20708 assignDocWin: function()
20710 var iframe = this.iframe;
20713 this.doc = iframe.contentWindow.document;
20714 this.win = iframe.contentWindow;
20716 // if (!Roo.get(this.frameId)) {
20719 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20720 // this.win = Roo.get(this.frameId).dom.contentWindow;
20722 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20726 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20727 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20732 initEditor : function(){
20733 //console.log("INIT EDITOR");
20734 this.assignDocWin();
20738 this.doc.designMode="on";
20740 this.doc.write(this.getDocMarkup());
20743 var dbody = (this.doc.body || this.doc.documentElement);
20744 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20745 // this copies styles from the containing element into thsi one..
20746 // not sure why we need all of this..
20747 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20749 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20750 //ss['background-attachment'] = 'fixed'; // w3c
20751 dbody.bgProperties = 'fixed'; // ie
20752 //Roo.DomHelper.applyStyles(dbody, ss);
20753 Roo.EventManager.on(this.doc, {
20754 //'mousedown': this.onEditorEvent,
20755 'mouseup': this.onEditorEvent,
20756 'dblclick': this.onEditorEvent,
20757 'click': this.onEditorEvent,
20758 'keyup': this.onEditorEvent,
20763 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20765 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20766 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20768 this.initialized = true;
20770 this.owner.fireEvent('initialize', this);
20775 onDestroy : function(){
20781 //for (var i =0; i < this.toolbars.length;i++) {
20782 // // fixme - ask toolbars for heights?
20783 // this.toolbars[i].onDestroy();
20786 //this.wrap.dom.innerHTML = '';
20787 //this.wrap.remove();
20792 onFirstFocus : function(){
20794 this.assignDocWin();
20797 this.activated = true;
20800 if(Roo.isGecko){ // prevent silly gecko errors
20802 var s = this.win.getSelection();
20803 if(!s.focusNode || s.focusNode.nodeType != 3){
20804 var r = s.getRangeAt(0);
20805 r.selectNodeContents((this.doc.body || this.doc.documentElement));
20810 this.execCmd('useCSS', true);
20811 this.execCmd('styleWithCSS', false);
20814 this.owner.fireEvent('activate', this);
20818 adjustFont: function(btn){
20819 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
20820 //if(Roo.isSafari){ // safari
20823 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
20824 if(Roo.isSafari){ // safari
20825 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
20826 v = (v < 10) ? 10 : v;
20827 v = (v > 48) ? 48 : v;
20828 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
20833 v = Math.max(1, v+adjust);
20835 this.execCmd('FontSize', v );
20838 onEditorEvent : function(e)
20840 this.owner.fireEvent('editorevent', this, e);
20841 // this.updateToolbar();
20842 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
20845 insertTag : function(tg)
20847 // could be a bit smarter... -> wrap the current selected tRoo..
20848 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
20850 range = this.createRange(this.getSelection());
20851 var wrappingNode = this.doc.createElement(tg.toLowerCase());
20852 wrappingNode.appendChild(range.extractContents());
20853 range.insertNode(wrappingNode);
20860 this.execCmd("formatblock", tg);
20864 insertText : function(txt)
20868 var range = this.createRange();
20869 range.deleteContents();
20870 //alert(Sender.getAttribute('label'));
20872 range.insertNode(this.doc.createTextNode(txt));
20878 * Executes a Midas editor command on the editor document and performs necessary focus and
20879 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
20880 * @param {String} cmd The Midas command
20881 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20883 relayCmd : function(cmd, value){
20885 this.execCmd(cmd, value);
20886 this.owner.fireEvent('editorevent', this);
20887 //this.updateToolbar();
20888 this.owner.deferFocus();
20892 * Executes a Midas editor command directly on the editor document.
20893 * For visual commands, you should use {@link #relayCmd} instead.
20894 * <b>This should only be called after the editor is initialized.</b>
20895 * @param {String} cmd The Midas command
20896 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20898 execCmd : function(cmd, value){
20899 this.doc.execCommand(cmd, false, value === undefined ? null : value);
20906 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
20908 * @param {String} text | dom node..
20910 insertAtCursor : function(text)
20915 if(!this.activated){
20921 var r = this.doc.selection.createRange();
20932 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
20936 // from jquery ui (MIT licenced)
20938 var win = this.win;
20940 if (win.getSelection && win.getSelection().getRangeAt) {
20941 range = win.getSelection().getRangeAt(0);
20942 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
20943 range.insertNode(node);
20944 } else if (win.document.selection && win.document.selection.createRange) {
20945 // no firefox support
20946 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20947 win.document.selection.createRange().pasteHTML(txt);
20949 // no firefox support
20950 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20951 this.execCmd('InsertHTML', txt);
20960 mozKeyPress : function(e){
20962 var c = e.getCharCode(), cmd;
20965 c = String.fromCharCode(c).toLowerCase();
20979 this.cleanUpPaste.defer(100, this);
20987 e.preventDefault();
20995 fixKeys : function(){ // load time branching for fastest keydown performance
20997 return function(e){
20998 var k = e.getKey(), r;
21001 r = this.doc.selection.createRange();
21004 r.pasteHTML('    ');
21011 r = this.doc.selection.createRange();
21013 var target = r.parentElement();
21014 if(!target || target.tagName.toLowerCase() != 'li'){
21016 r.pasteHTML('<br />');
21022 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21023 this.cleanUpPaste.defer(100, this);
21029 }else if(Roo.isOpera){
21030 return function(e){
21031 var k = e.getKey();
21035 this.execCmd('InsertHTML','    ');
21038 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21039 this.cleanUpPaste.defer(100, this);
21044 }else if(Roo.isSafari){
21045 return function(e){
21046 var k = e.getKey();
21050 this.execCmd('InsertText','\t');
21054 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21055 this.cleanUpPaste.defer(100, this);
21063 getAllAncestors: function()
21065 var p = this.getSelectedNode();
21068 a.push(p); // push blank onto stack..
21069 p = this.getParentElement();
21073 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21077 a.push(this.doc.body);
21081 lastSelNode : false,
21084 getSelection : function()
21086 this.assignDocWin();
21087 return Roo.isIE ? this.doc.selection : this.win.getSelection();
21090 getSelectedNode: function()
21092 // this may only work on Gecko!!!
21094 // should we cache this!!!!
21099 var range = this.createRange(this.getSelection()).cloneRange();
21102 var parent = range.parentElement();
21104 var testRange = range.duplicate();
21105 testRange.moveToElementText(parent);
21106 if (testRange.inRange(range)) {
21109 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21112 parent = parent.parentElement;
21117 // is ancestor a text element.
21118 var ac = range.commonAncestorContainer;
21119 if (ac.nodeType == 3) {
21120 ac = ac.parentNode;
21123 var ar = ac.childNodes;
21126 var other_nodes = [];
21127 var has_other_nodes = false;
21128 for (var i=0;i<ar.length;i++) {
21129 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
21132 // fullly contained node.
21134 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21139 // probably selected..
21140 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21141 other_nodes.push(ar[i]);
21145 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
21150 has_other_nodes = true;
21152 if (!nodes.length && other_nodes.length) {
21153 nodes= other_nodes;
21155 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21161 createRange: function(sel)
21163 // this has strange effects when using with
21164 // top toolbar - not sure if it's a great idea.
21165 //this.editor.contentWindow.focus();
21166 if (typeof sel != "undefined") {
21168 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21170 return this.doc.createRange();
21173 return this.doc.createRange();
21176 getParentElement: function()
21179 this.assignDocWin();
21180 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21182 var range = this.createRange(sel);
21185 var p = range.commonAncestorContainer;
21186 while (p.nodeType == 3) { // text node
21197 * Range intersection.. the hard stuff...
21201 * [ -- selected range --- ]
21205 * if end is before start or hits it. fail.
21206 * if start is after end or hits it fail.
21208 * if either hits (but other is outside. - then it's not
21214 // @see http://www.thismuchiknow.co.uk/?p=64.
21215 rangeIntersectsNode : function(range, node)
21217 var nodeRange = node.ownerDocument.createRange();
21219 nodeRange.selectNode(node);
21221 nodeRange.selectNodeContents(node);
21224 var rangeStartRange = range.cloneRange();
21225 rangeStartRange.collapse(true);
21227 var rangeEndRange = range.cloneRange();
21228 rangeEndRange.collapse(false);
21230 var nodeStartRange = nodeRange.cloneRange();
21231 nodeStartRange.collapse(true);
21233 var nodeEndRange = nodeRange.cloneRange();
21234 nodeEndRange.collapse(false);
21236 return rangeStartRange.compareBoundaryPoints(
21237 Range.START_TO_START, nodeEndRange) == -1 &&
21238 rangeEndRange.compareBoundaryPoints(
21239 Range.START_TO_START, nodeStartRange) == 1;
21243 rangeCompareNode : function(range, node)
21245 var nodeRange = node.ownerDocument.createRange();
21247 nodeRange.selectNode(node);
21249 nodeRange.selectNodeContents(node);
21253 range.collapse(true);
21255 nodeRange.collapse(true);
21257 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21258 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
21260 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21262 var nodeIsBefore = ss == 1;
21263 var nodeIsAfter = ee == -1;
21265 if (nodeIsBefore && nodeIsAfter) {
21268 if (!nodeIsBefore && nodeIsAfter) {
21269 return 1; //right trailed.
21272 if (nodeIsBefore && !nodeIsAfter) {
21273 return 2; // left trailed.
21279 // private? - in a new class?
21280 cleanUpPaste : function()
21282 // cleans up the whole document..
21283 Roo.log('cleanuppaste');
21285 this.cleanUpChildren(this.doc.body);
21286 var clean = this.cleanWordChars(this.doc.body.innerHTML);
21287 if (clean != this.doc.body.innerHTML) {
21288 this.doc.body.innerHTML = clean;
21293 cleanWordChars : function(input) {// change the chars to hex code
21294 var he = Roo.HtmlEditorCore;
21296 var output = input;
21297 Roo.each(he.swapCodes, function(sw) {
21298 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21300 output = output.replace(swapper, sw[1]);
21307 cleanUpChildren : function (n)
21309 if (!n.childNodes.length) {
21312 for (var i = n.childNodes.length-1; i > -1 ; i--) {
21313 this.cleanUpChild(n.childNodes[i]);
21320 cleanUpChild : function (node)
21323 //console.log(node);
21324 if (node.nodeName == "#text") {
21325 // clean up silly Windows -- stuff?
21328 if (node.nodeName == "#comment") {
21329 node.parentNode.removeChild(node);
21330 // clean up silly Windows -- stuff?
21333 var lcname = node.tagName.toLowerCase();
21334 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21335 // whitelist of tags..
21337 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21339 node.parentNode.removeChild(node);
21344 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21346 // remove <a name=....> as rendering on yahoo mailer is borked with this.
21347 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21349 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21350 // remove_keep_children = true;
21353 if (remove_keep_children) {
21354 this.cleanUpChildren(node);
21355 // inserts everything just before this node...
21356 while (node.childNodes.length) {
21357 var cn = node.childNodes[0];
21358 node.removeChild(cn);
21359 node.parentNode.insertBefore(cn, node);
21361 node.parentNode.removeChild(node);
21365 if (!node.attributes || !node.attributes.length) {
21366 this.cleanUpChildren(node);
21370 function cleanAttr(n,v)
21373 if (v.match(/^\./) || v.match(/^\//)) {
21376 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
21379 if (v.match(/^#/)) {
21382 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21383 node.removeAttribute(n);
21387 var cwhite = this.cwhite;
21388 var cblack = this.cblack;
21390 function cleanStyle(n,v)
21392 if (v.match(/expression/)) { //XSS?? should we even bother..
21393 node.removeAttribute(n);
21397 var parts = v.split(/;/);
21400 Roo.each(parts, function(p) {
21401 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21405 var l = p.split(':').shift().replace(/\s+/g,'');
21406 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21408 if ( cwhite.length && cblack.indexOf(l) > -1) {
21409 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21410 //node.removeAttribute(n);
21414 // only allow 'c whitelisted system attributes'
21415 if ( cwhite.length && cwhite.indexOf(l) < 0) {
21416 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21417 //node.removeAttribute(n);
21427 if (clean.length) {
21428 node.setAttribute(n, clean.join(';'));
21430 node.removeAttribute(n);
21436 for (var i = node.attributes.length-1; i > -1 ; i--) {
21437 var a = node.attributes[i];
21440 if (a.name.toLowerCase().substr(0,2)=='on') {
21441 node.removeAttribute(a.name);
21444 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21445 node.removeAttribute(a.name);
21448 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21449 cleanAttr(a.name,a.value); // fixme..
21452 if (a.name == 'style') {
21453 cleanStyle(a.name,a.value);
21456 /// clean up MS crap..
21457 // tecnically this should be a list of valid class'es..
21460 if (a.name == 'class') {
21461 if (a.value.match(/^Mso/)) {
21462 node.className = '';
21465 if (a.value.match(/body/)) {
21466 node.className = '';
21477 this.cleanUpChildren(node);
21483 * Clean up MS wordisms...
21485 cleanWord : function(node)
21490 this.cleanWord(this.doc.body);
21493 if (node.nodeName == "#text") {
21494 // clean up silly Windows -- stuff?
21497 if (node.nodeName == "#comment") {
21498 node.parentNode.removeChild(node);
21499 // clean up silly Windows -- stuff?
21503 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21504 node.parentNode.removeChild(node);
21508 // remove - but keep children..
21509 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
21510 while (node.childNodes.length) {
21511 var cn = node.childNodes[0];
21512 node.removeChild(cn);
21513 node.parentNode.insertBefore(cn, node);
21515 node.parentNode.removeChild(node);
21516 this.iterateChildren(node, this.cleanWord);
21520 if (node.className.length) {
21522 var cn = node.className.split(/\W+/);
21524 Roo.each(cn, function(cls) {
21525 if (cls.match(/Mso[a-zA-Z]+/)) {
21530 node.className = cna.length ? cna.join(' ') : '';
21532 node.removeAttribute("class");
21536 if (node.hasAttribute("lang")) {
21537 node.removeAttribute("lang");
21540 if (node.hasAttribute("style")) {
21542 var styles = node.getAttribute("style").split(";");
21544 Roo.each(styles, function(s) {
21545 if (!s.match(/:/)) {
21548 var kv = s.split(":");
21549 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21552 // what ever is left... we allow.
21555 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21556 if (!nstyle.length) {
21557 node.removeAttribute('style');
21560 this.iterateChildren(node, this.cleanWord);
21566 * iterateChildren of a Node, calling fn each time, using this as the scole..
21567 * @param {DomNode} node node to iterate children of.
21568 * @param {Function} fn method of this class to call on each item.
21570 iterateChildren : function(node, fn)
21572 if (!node.childNodes.length) {
21575 for (var i = node.childNodes.length-1; i > -1 ; i--) {
21576 fn.call(this, node.childNodes[i])
21582 * cleanTableWidths.
21584 * Quite often pasting from word etc.. results in tables with column and widths.
21585 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21588 cleanTableWidths : function(node)
21593 this.cleanTableWidths(this.doc.body);
21598 if (node.nodeName == "#text" || node.nodeName == "#comment") {
21601 Roo.log(node.tagName);
21602 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21603 this.iterateChildren(node, this.cleanTableWidths);
21606 if (node.hasAttribute('width')) {
21607 node.removeAttribute('width');
21611 if (node.hasAttribute("style")) {
21614 var styles = node.getAttribute("style").split(";");
21616 Roo.each(styles, function(s) {
21617 if (!s.match(/:/)) {
21620 var kv = s.split(":");
21621 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21624 // what ever is left... we allow.
21627 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21628 if (!nstyle.length) {
21629 node.removeAttribute('style');
21633 this.iterateChildren(node, this.cleanTableWidths);
21641 domToHTML : function(currentElement, depth, nopadtext) {
21643 depth = depth || 0;
21644 nopadtext = nopadtext || false;
21646 if (!currentElement) {
21647 return this.domToHTML(this.doc.body);
21650 //Roo.log(currentElement);
21652 var allText = false;
21653 var nodeName = currentElement.nodeName;
21654 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21656 if (nodeName == '#text') {
21658 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21663 if (nodeName != 'BODY') {
21666 // Prints the node tagName, such as <A>, <IMG>, etc
21669 for(i = 0; i < currentElement.attributes.length;i++) {
21671 var aname = currentElement.attributes.item(i).name;
21672 if (!currentElement.attributes.item(i).value.length) {
21675 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21678 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21687 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21690 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21695 // Traverse the tree
21697 var currentElementChild = currentElement.childNodes.item(i);
21698 var allText = true;
21699 var innerHTML = '';
21701 while (currentElementChild) {
21702 // Formatting code (indent the tree so it looks nice on the screen)
21703 var nopad = nopadtext;
21704 if (lastnode == 'SPAN') {
21708 if (currentElementChild.nodeName == '#text') {
21709 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21710 toadd = nopadtext ? toadd : toadd.trim();
21711 if (!nopad && toadd.length > 80) {
21712 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
21714 innerHTML += toadd;
21717 currentElementChild = currentElement.childNodes.item(i);
21723 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
21725 // Recursively traverse the tree structure of the child node
21726 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
21727 lastnode = currentElementChild.nodeName;
21729 currentElementChild=currentElement.childNodes.item(i);
21735 // The remaining code is mostly for formatting the tree
21736 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
21741 ret+= "</"+tagName+">";
21747 applyBlacklists : function()
21749 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
21750 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
21754 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
21755 if (b.indexOf(tag) > -1) {
21758 this.white.push(tag);
21762 Roo.each(w, function(tag) {
21763 if (b.indexOf(tag) > -1) {
21766 if (this.white.indexOf(tag) > -1) {
21769 this.white.push(tag);
21774 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
21775 if (w.indexOf(tag) > -1) {
21778 this.black.push(tag);
21782 Roo.each(b, function(tag) {
21783 if (w.indexOf(tag) > -1) {
21786 if (this.black.indexOf(tag) > -1) {
21789 this.black.push(tag);
21794 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
21795 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
21799 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
21800 if (b.indexOf(tag) > -1) {
21803 this.cwhite.push(tag);
21807 Roo.each(w, function(tag) {
21808 if (b.indexOf(tag) > -1) {
21811 if (this.cwhite.indexOf(tag) > -1) {
21814 this.cwhite.push(tag);
21819 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
21820 if (w.indexOf(tag) > -1) {
21823 this.cblack.push(tag);
21827 Roo.each(b, function(tag) {
21828 if (w.indexOf(tag) > -1) {
21831 if (this.cblack.indexOf(tag) > -1) {
21834 this.cblack.push(tag);
21839 setStylesheets : function(stylesheets)
21841 if(typeof(stylesheets) == 'string'){
21842 Roo.get(this.iframe.contentDocument.head).createChild({
21844 rel : 'stylesheet',
21853 Roo.each(stylesheets, function(s) {
21858 Roo.get(_this.iframe.contentDocument.head).createChild({
21860 rel : 'stylesheet',
21869 removeStylesheets : function()
21873 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
21878 // hide stuff that is not compatible
21892 * @event specialkey
21896 * @cfg {String} fieldClass @hide
21899 * @cfg {String} focusClass @hide
21902 * @cfg {String} autoCreate @hide
21905 * @cfg {String} inputType @hide
21908 * @cfg {String} invalidClass @hide
21911 * @cfg {String} invalidText @hide
21914 * @cfg {String} msgFx @hide
21917 * @cfg {String} validateOnBlur @hide
21921 Roo.HtmlEditorCore.white = [
21922 'area', 'br', 'img', 'input', 'hr', 'wbr',
21924 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
21925 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
21926 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
21927 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
21928 'table', 'ul', 'xmp',
21930 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
21933 'dir', 'menu', 'ol', 'ul', 'dl',
21939 Roo.HtmlEditorCore.black = [
21940 // 'embed', 'object', // enable - backend responsiblity to clean thiese
21942 'base', 'basefont', 'bgsound', 'blink', 'body',
21943 'frame', 'frameset', 'head', 'html', 'ilayer',
21944 'iframe', 'layer', 'link', 'meta', 'object',
21945 'script', 'style' ,'title', 'xml' // clean later..
21947 Roo.HtmlEditorCore.clean = [
21948 'script', 'style', 'title', 'xml'
21950 Roo.HtmlEditorCore.remove = [
21955 Roo.HtmlEditorCore.ablack = [
21959 Roo.HtmlEditorCore.aclean = [
21960 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
21964 Roo.HtmlEditorCore.pwhite= [
21965 'http', 'https', 'mailto'
21968 // white listed style attributes.
21969 Roo.HtmlEditorCore.cwhite= [
21970 // 'text-align', /// default is to allow most things..
21976 // black listed style attributes.
21977 Roo.HtmlEditorCore.cblack= [
21978 // 'font-size' -- this can be set by the project
21982 Roo.HtmlEditorCore.swapCodes =[
22001 * @class Roo.bootstrap.HtmlEditor
22002 * @extends Roo.bootstrap.TextArea
22003 * Bootstrap HtmlEditor class
22006 * Create a new HtmlEditor
22007 * @param {Object} config The config object
22010 Roo.bootstrap.HtmlEditor = function(config){
22011 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
22012 if (!this.toolbars) {
22013 this.toolbars = [];
22015 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22018 * @event initialize
22019 * Fires when the editor is fully initialized (including the iframe)
22020 * @param {HtmlEditor} this
22025 * Fires when the editor is first receives the focus. Any insertion must wait
22026 * until after this event.
22027 * @param {HtmlEditor} this
22031 * @event beforesync
22032 * Fires before the textarea is updated with content from the editor iframe. Return false
22033 * to cancel the sync.
22034 * @param {HtmlEditor} this
22035 * @param {String} html
22039 * @event beforepush
22040 * Fires before the iframe editor is updated with content from the textarea. Return false
22041 * to cancel the push.
22042 * @param {HtmlEditor} this
22043 * @param {String} html
22048 * Fires when the textarea is updated with content from the editor iframe.
22049 * @param {HtmlEditor} this
22050 * @param {String} html
22055 * Fires when the iframe editor is updated with content from the textarea.
22056 * @param {HtmlEditor} this
22057 * @param {String} html
22061 * @event editmodechange
22062 * Fires when the editor switches edit modes
22063 * @param {HtmlEditor} this
22064 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22066 editmodechange: true,
22068 * @event editorevent
22069 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22070 * @param {HtmlEditor} this
22074 * @event firstfocus
22075 * Fires when on first focus - needed by toolbars..
22076 * @param {HtmlEditor} this
22081 * Auto save the htmlEditor value as a file into Events
22082 * @param {HtmlEditor} this
22086 * @event savedpreview
22087 * preview the saved version of htmlEditor
22088 * @param {HtmlEditor} this
22095 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
22099 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22104 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
22109 * @cfg {Number} height (in pixels)
22113 * @cfg {Number} width (in pixels)
22118 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22121 stylesheets: false,
22126 // private properties
22127 validationEvent : false,
22129 initialized : false,
22132 onFocus : Roo.emptyFn,
22134 hideMode:'offsets',
22137 tbContainer : false,
22139 toolbarContainer :function() {
22140 return this.wrap.select('.x-html-editor-tb',true).first();
22144 * Protected method that will not generally be called directly. It
22145 * is called when the editor creates its toolbar. Override this method if you need to
22146 * add custom toolbar buttons.
22147 * @param {HtmlEditor} editor
22149 createToolbar : function(){
22151 Roo.log("create toolbars");
22153 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
22154 this.toolbars[0].render(this.toolbarContainer());
22158 // if (!editor.toolbars || !editor.toolbars.length) {
22159 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
22162 // for (var i =0 ; i < editor.toolbars.length;i++) {
22163 // editor.toolbars[i] = Roo.factory(
22164 // typeof(editor.toolbars[i]) == 'string' ?
22165 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
22166 // Roo.bootstrap.HtmlEditor);
22167 // editor.toolbars[i].init(editor);
22173 onRender : function(ct, position)
22175 // Roo.log("Call onRender: " + this.xtype);
22177 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
22179 this.wrap = this.inputEl().wrap({
22180 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22183 this.editorcore.onRender(ct, position);
22185 if (this.resizable) {
22186 this.resizeEl = new Roo.Resizable(this.wrap, {
22190 minHeight : this.height,
22191 height: this.height,
22192 handles : this.resizable,
22195 resize : function(r, w, h) {
22196 _t.onResize(w,h); // -something
22202 this.createToolbar(this);
22205 if(!this.width && this.resizable){
22206 this.setSize(this.wrap.getSize());
22208 if (this.resizeEl) {
22209 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22210 // should trigger onReize..
22216 onResize : function(w, h)
22218 Roo.log('resize: ' +w + ',' + h );
22219 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
22223 if(this.inputEl() ){
22224 if(typeof w == 'number'){
22225 var aw = w - this.wrap.getFrameWidth('lr');
22226 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
22229 if(typeof h == 'number'){
22230 var tbh = -11; // fixme it needs to tool bar size!
22231 for (var i =0; i < this.toolbars.length;i++) {
22232 // fixme - ask toolbars for heights?
22233 tbh += this.toolbars[i].el.getHeight();
22234 //if (this.toolbars[i].footer) {
22235 // tbh += this.toolbars[i].footer.el.getHeight();
22243 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
22244 ah -= 5; // knock a few pixes off for look..
22245 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
22249 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
22250 this.editorcore.onResize(ew,eh);
22255 * Toggles the editor between standard and source edit mode.
22256 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22258 toggleSourceEdit : function(sourceEditMode)
22260 this.editorcore.toggleSourceEdit(sourceEditMode);
22262 if(this.editorcore.sourceEditMode){
22263 Roo.log('editor - showing textarea');
22266 // Roo.log(this.syncValue());
22268 this.inputEl().removeClass(['hide', 'x-hidden']);
22269 this.inputEl().dom.removeAttribute('tabIndex');
22270 this.inputEl().focus();
22272 Roo.log('editor - hiding textarea');
22274 // Roo.log(this.pushValue());
22277 this.inputEl().addClass(['hide', 'x-hidden']);
22278 this.inputEl().dom.setAttribute('tabIndex', -1);
22279 //this.deferFocus();
22282 if(this.resizable){
22283 this.setSize(this.wrap.getSize());
22286 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
22289 // private (for BoxComponent)
22290 adjustSize : Roo.BoxComponent.prototype.adjustSize,
22292 // private (for BoxComponent)
22293 getResizeEl : function(){
22297 // private (for BoxComponent)
22298 getPositionEl : function(){
22303 initEvents : function(){
22304 this.originalValue = this.getValue();
22308 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22311 // markInvalid : Roo.emptyFn,
22313 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22316 // clearInvalid : Roo.emptyFn,
22318 setValue : function(v){
22319 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
22320 this.editorcore.pushValue();
22325 deferFocus : function(){
22326 this.focus.defer(10, this);
22330 focus : function(){
22331 this.editorcore.focus();
22337 onDestroy : function(){
22343 for (var i =0; i < this.toolbars.length;i++) {
22344 // fixme - ask toolbars for heights?
22345 this.toolbars[i].onDestroy();
22348 this.wrap.dom.innerHTML = '';
22349 this.wrap.remove();
22354 onFirstFocus : function(){
22355 //Roo.log("onFirstFocus");
22356 this.editorcore.onFirstFocus();
22357 for (var i =0; i < this.toolbars.length;i++) {
22358 this.toolbars[i].onFirstFocus();
22364 syncValue : function()
22366 this.editorcore.syncValue();
22369 pushValue : function()
22371 this.editorcore.pushValue();
22375 // hide stuff that is not compatible
22389 * @event specialkey
22393 * @cfg {String} fieldClass @hide
22396 * @cfg {String} focusClass @hide
22399 * @cfg {String} autoCreate @hide
22402 * @cfg {String} inputType @hide
22405 * @cfg {String} invalidClass @hide
22408 * @cfg {String} invalidText @hide
22411 * @cfg {String} msgFx @hide
22414 * @cfg {String} validateOnBlur @hide
22423 Roo.namespace('Roo.bootstrap.htmleditor');
22425 * @class Roo.bootstrap.HtmlEditorToolbar1
22430 new Roo.bootstrap.HtmlEditor({
22433 new Roo.bootstrap.HtmlEditorToolbar1({
22434 disable : { fonts: 1 , format: 1, ..., ... , ...],
22440 * @cfg {Object} disable List of elements to disable..
22441 * @cfg {Array} btns List of additional buttons.
22445 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22448 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
22451 Roo.apply(this, config);
22453 // default disabled, based on 'good practice'..
22454 this.disable = this.disable || {};
22455 Roo.applyIf(this.disable, {
22458 specialElements : true
22460 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
22462 this.editor = config.editor;
22463 this.editorcore = config.editor.editorcore;
22465 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
22467 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22468 // dont call parent... till later.
22470 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
22475 editorcore : false,
22480 "h1","h2","h3","h4","h5","h6",
22482 "abbr", "acronym", "address", "cite", "samp", "var",
22486 onRender : function(ct, position)
22488 // Roo.log("Call onRender: " + this.xtype);
22490 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
22492 this.el.dom.style.marginBottom = '0';
22494 var editorcore = this.editorcore;
22495 var editor= this.editor;
22498 var btn = function(id,cmd , toggle, handler){
22500 var event = toggle ? 'toggle' : 'click';
22505 xns: Roo.bootstrap,
22508 enableToggle:toggle !== false,
22510 pressed : toggle ? false : null,
22513 a.listeners[toggle ? 'toggle' : 'click'] = function() {
22514 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
22523 xns: Roo.bootstrap,
22524 glyphicon : 'font',
22528 xns: Roo.bootstrap,
22532 Roo.each(this.formats, function(f) {
22533 style.menu.items.push({
22535 xns: Roo.bootstrap,
22536 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
22541 editorcore.insertTag(this.tagname);
22548 children.push(style);
22551 btn('bold',false,true);
22552 btn('italic',false,true);
22553 btn('align-left', 'justifyleft',true);
22554 btn('align-center', 'justifycenter',true);
22555 btn('align-right' , 'justifyright',true);
22556 btn('link', false, false, function(btn) {
22557 //Roo.log("create link?");
22558 var url = prompt(this.createLinkText, this.defaultLinkValue);
22559 if(url && url != 'http:/'+'/'){
22560 this.editorcore.relayCmd('createlink', url);
22563 btn('list','insertunorderedlist',true);
22564 btn('pencil', false,true, function(btn){
22567 this.toggleSourceEdit(btn.pressed);
22573 xns: Roo.bootstrap,
22578 xns: Roo.bootstrap,
22583 cog.menu.items.push({
22585 xns: Roo.bootstrap,
22586 html : Clean styles,
22591 editorcore.insertTag(this.tagname);
22600 this.xtype = 'NavSimplebar';
22602 for(var i=0;i< children.length;i++) {
22604 this.buttons.add(this.addxtypeChild(children[i]));
22608 editor.on('editorevent', this.updateToolbar, this);
22610 onBtnClick : function(id)
22612 this.editorcore.relayCmd(id);
22613 this.editorcore.focus();
22617 * Protected method that will not generally be called directly. It triggers
22618 * a toolbar update by reading the markup state of the current selection in the editor.
22620 updateToolbar: function(){
22622 if(!this.editorcore.activated){
22623 this.editor.onFirstFocus(); // is this neeed?
22627 var btns = this.buttons;
22628 var doc = this.editorcore.doc;
22629 btns.get('bold').setActive(doc.queryCommandState('bold'));
22630 btns.get('italic').setActive(doc.queryCommandState('italic'));
22631 //btns.get('underline').setActive(doc.queryCommandState('underline'));
22633 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
22634 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
22635 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
22637 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
22638 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
22641 var ans = this.editorcore.getAllAncestors();
22642 if (this.formatCombo) {
22645 var store = this.formatCombo.store;
22646 this.formatCombo.setValue("");
22647 for (var i =0; i < ans.length;i++) {
22648 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
22650 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
22658 // hides menus... - so this cant be on a menu...
22659 Roo.bootstrap.MenuMgr.hideAll();
22661 Roo.bootstrap.MenuMgr.hideAll();
22662 //this.editorsyncValue();
22664 onFirstFocus: function() {
22665 this.buttons.each(function(item){
22669 toggleSourceEdit : function(sourceEditMode){
22672 if(sourceEditMode){
22673 Roo.log("disabling buttons");
22674 this.buttons.each( function(item){
22675 if(item.cmd != 'pencil'){
22681 Roo.log("enabling buttons");
22682 if(this.editorcore.initialized){
22683 this.buttons.each( function(item){
22689 Roo.log("calling toggole on editor");
22690 // tell the editor that it's been pressed..
22691 this.editor.toggleSourceEdit(sourceEditMode);
22701 * @class Roo.bootstrap.Table.AbstractSelectionModel
22702 * @extends Roo.util.Observable
22703 * Abstract base class for grid SelectionModels. It provides the interface that should be
22704 * implemented by descendant classes. This class should not be directly instantiated.
22707 Roo.bootstrap.Table.AbstractSelectionModel = function(){
22708 this.locked = false;
22709 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
22713 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
22714 /** @ignore Called by the grid automatically. Do not call directly. */
22715 init : function(grid){
22721 * Locks the selections.
22724 this.locked = true;
22728 * Unlocks the selections.
22730 unlock : function(){
22731 this.locked = false;
22735 * Returns true if the selections are locked.
22736 * @return {Boolean}
22738 isLocked : function(){
22739 return this.locked;
22743 * @extends Roo.bootstrap.Table.AbstractSelectionModel
22744 * @class Roo.bootstrap.Table.RowSelectionModel
22745 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
22746 * It supports multiple selections and keyboard selection/navigation.
22748 * @param {Object} config
22751 Roo.bootstrap.Table.RowSelectionModel = function(config){
22752 Roo.apply(this, config);
22753 this.selections = new Roo.util.MixedCollection(false, function(o){
22758 this.lastActive = false;
22762 * @event selectionchange
22763 * Fires when the selection changes
22764 * @param {SelectionModel} this
22766 "selectionchange" : true,
22768 * @event afterselectionchange
22769 * Fires after the selection changes (eg. by key press or clicking)
22770 * @param {SelectionModel} this
22772 "afterselectionchange" : true,
22774 * @event beforerowselect
22775 * Fires when a row is selected being selected, return false to cancel.
22776 * @param {SelectionModel} this
22777 * @param {Number} rowIndex The selected index
22778 * @param {Boolean} keepExisting False if other selections will be cleared
22780 "beforerowselect" : true,
22783 * Fires when a row is selected.
22784 * @param {SelectionModel} this
22785 * @param {Number} rowIndex The selected index
22786 * @param {Roo.data.Record} r The record
22788 "rowselect" : true,
22790 * @event rowdeselect
22791 * Fires when a row is deselected.
22792 * @param {SelectionModel} this
22793 * @param {Number} rowIndex The selected index
22795 "rowdeselect" : true
22797 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
22798 this.locked = false;
22801 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
22803 * @cfg {Boolean} singleSelect
22804 * True to allow selection of only one row at a time (defaults to false)
22806 singleSelect : false,
22809 initEvents : function()
22812 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
22813 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
22814 //}else{ // allow click to work like normal
22815 // this.grid.on("rowclick", this.handleDragableRowClick, this);
22817 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
22818 this.grid.on("rowclick", this.handleMouseDown, this);
22820 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
22821 "up" : function(e){
22823 this.selectPrevious(e.shiftKey);
22824 }else if(this.last !== false && this.lastActive !== false){
22825 var last = this.last;
22826 this.selectRange(this.last, this.lastActive-1);
22827 this.grid.getView().focusRow(this.lastActive);
22828 if(last !== false){
22832 this.selectFirstRow();
22834 this.fireEvent("afterselectionchange", this);
22836 "down" : function(e){
22838 this.selectNext(e.shiftKey);
22839 }else if(this.last !== false && this.lastActive !== false){
22840 var last = this.last;
22841 this.selectRange(this.last, this.lastActive+1);
22842 this.grid.getView().focusRow(this.lastActive);
22843 if(last !== false){
22847 this.selectFirstRow();
22849 this.fireEvent("afterselectionchange", this);
22853 this.grid.store.on('load', function(){
22854 this.selections.clear();
22857 var view = this.grid.view;
22858 view.on("refresh", this.onRefresh, this);
22859 view.on("rowupdated", this.onRowUpdated, this);
22860 view.on("rowremoved", this.onRemove, this);
22865 onRefresh : function()
22867 var ds = this.grid.store, i, v = this.grid.view;
22868 var s = this.selections;
22869 s.each(function(r){
22870 if((i = ds.indexOfId(r.id)) != -1){
22879 onRemove : function(v, index, r){
22880 this.selections.remove(r);
22884 onRowUpdated : function(v, index, r){
22885 if(this.isSelected(r)){
22886 v.onRowSelect(index);
22892 * @param {Array} records The records to select
22893 * @param {Boolean} keepExisting (optional) True to keep existing selections
22895 selectRecords : function(records, keepExisting)
22898 this.clearSelections();
22900 var ds = this.grid.store;
22901 for(var i = 0, len = records.length; i < len; i++){
22902 this.selectRow(ds.indexOf(records[i]), true);
22907 * Gets the number of selected rows.
22910 getCount : function(){
22911 return this.selections.length;
22915 * Selects the first row in the grid.
22917 selectFirstRow : function(){
22922 * Select the last row.
22923 * @param {Boolean} keepExisting (optional) True to keep existing selections
22925 selectLastRow : function(keepExisting){
22926 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
22927 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
22931 * Selects the row immediately following the last selected row.
22932 * @param {Boolean} keepExisting (optional) True to keep existing selections
22934 selectNext : function(keepExisting)
22936 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
22937 this.selectRow(this.last+1, keepExisting);
22938 this.grid.getView().focusRow(this.last);
22943 * Selects the row that precedes the last selected row.
22944 * @param {Boolean} keepExisting (optional) True to keep existing selections
22946 selectPrevious : function(keepExisting){
22948 this.selectRow(this.last-1, keepExisting);
22949 this.grid.getView().focusRow(this.last);
22954 * Returns the selected records
22955 * @return {Array} Array of selected records
22957 getSelections : function(){
22958 return [].concat(this.selections.items);
22962 * Returns the first selected record.
22965 getSelected : function(){
22966 return this.selections.itemAt(0);
22971 * Clears all selections.
22973 clearSelections : function(fast)
22979 var ds = this.grid.store;
22980 var s = this.selections;
22981 s.each(function(r){
22982 this.deselectRow(ds.indexOfId(r.id));
22986 this.selections.clear();
22993 * Selects all rows.
22995 selectAll : function(){
22999 this.selections.clear();
23000 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
23001 this.selectRow(i, true);
23006 * Returns True if there is a selection.
23007 * @return {Boolean}
23009 hasSelection : function(){
23010 return this.selections.length > 0;
23014 * Returns True if the specified row is selected.
23015 * @param {Number/Record} record The record or index of the record to check
23016 * @return {Boolean}
23018 isSelected : function(index){
23019 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
23020 return (r && this.selections.key(r.id) ? true : false);
23024 * Returns True if the specified record id is selected.
23025 * @param {String} id The id of record to check
23026 * @return {Boolean}
23028 isIdSelected : function(id){
23029 return (this.selections.key(id) ? true : false);
23034 handleMouseDBClick : function(e, t){
23038 handleMouseDown : function(e, t)
23040 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
23041 if(this.isLocked() || rowIndex < 0 ){
23044 if(e.shiftKey && this.last !== false){
23045 var last = this.last;
23046 this.selectRange(last, rowIndex, e.ctrlKey);
23047 this.last = last; // reset the last
23051 var isSelected = this.isSelected(rowIndex);
23052 //Roo.log("select row:" + rowIndex);
23054 this.deselectRow(rowIndex);
23056 this.selectRow(rowIndex, true);
23060 if(e.button !== 0 && isSelected){
23061 alert('rowIndex 2: ' + rowIndex);
23062 view.focusRow(rowIndex);
23063 }else if(e.ctrlKey && isSelected){
23064 this.deselectRow(rowIndex);
23065 }else if(!isSelected){
23066 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
23067 view.focusRow(rowIndex);
23071 this.fireEvent("afterselectionchange", this);
23074 handleDragableRowClick : function(grid, rowIndex, e)
23076 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
23077 this.selectRow(rowIndex, false);
23078 grid.view.focusRow(rowIndex);
23079 this.fireEvent("afterselectionchange", this);
23084 * Selects multiple rows.
23085 * @param {Array} rows Array of the indexes of the row to select
23086 * @param {Boolean} keepExisting (optional) True to keep existing selections
23088 selectRows : function(rows, keepExisting){
23090 this.clearSelections();
23092 for(var i = 0, len = rows.length; i < len; i++){
23093 this.selectRow(rows[i], true);
23098 * Selects a range of rows. All rows in between startRow and endRow are also selected.
23099 * @param {Number} startRow The index of the first row in the range
23100 * @param {Number} endRow The index of the last row in the range
23101 * @param {Boolean} keepExisting (optional) True to retain existing selections
23103 selectRange : function(startRow, endRow, keepExisting){
23108 this.clearSelections();
23110 if(startRow <= endRow){
23111 for(var i = startRow; i <= endRow; i++){
23112 this.selectRow(i, true);
23115 for(var i = startRow; i >= endRow; i--){
23116 this.selectRow(i, true);
23122 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
23123 * @param {Number} startRow The index of the first row in the range
23124 * @param {Number} endRow The index of the last row in the range
23126 deselectRange : function(startRow, endRow, preventViewNotify){
23130 for(var i = startRow; i <= endRow; i++){
23131 this.deselectRow(i, preventViewNotify);
23137 * @param {Number} row The index of the row to select
23138 * @param {Boolean} keepExisting (optional) True to keep existing selections
23140 selectRow : function(index, keepExisting, preventViewNotify)
23142 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
23145 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
23146 if(!keepExisting || this.singleSelect){
23147 this.clearSelections();
23150 var r = this.grid.store.getAt(index);
23151 //console.log('selectRow - record id :' + r.id);
23153 this.selections.add(r);
23154 this.last = this.lastActive = index;
23155 if(!preventViewNotify){
23156 var proxy = new Roo.Element(
23157 this.grid.getRowDom(index)
23159 proxy.addClass('bg-info info');
23161 this.fireEvent("rowselect", this, index, r);
23162 this.fireEvent("selectionchange", this);
23168 * @param {Number} row The index of the row to deselect
23170 deselectRow : function(index, preventViewNotify)
23175 if(this.last == index){
23178 if(this.lastActive == index){
23179 this.lastActive = false;
23182 var r = this.grid.store.getAt(index);
23187 this.selections.remove(r);
23188 //.console.log('deselectRow - record id :' + r.id);
23189 if(!preventViewNotify){
23191 var proxy = new Roo.Element(
23192 this.grid.getRowDom(index)
23194 proxy.removeClass('bg-info info');
23196 this.fireEvent("rowdeselect", this, index);
23197 this.fireEvent("selectionchange", this);
23201 restoreLast : function(){
23203 this.last = this._last;
23208 acceptsNav : function(row, col, cm){
23209 return !cm.isHidden(col) && cm.isCellEditable(col, row);
23213 onEditorKey : function(field, e){
23214 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
23219 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
23221 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
23223 }else if(k == e.ENTER && !e.ctrlKey){
23227 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
23229 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
23231 }else if(k == e.ESC){
23235 g.startEditing(newCell[0], newCell[1]);
23241 * Ext JS Library 1.1.1
23242 * Copyright(c) 2006-2007, Ext JS, LLC.
23244 * Originally Released Under LGPL - original licence link has changed is not relivant.
23247 * <script type="text/javascript">
23251 * @class Roo.bootstrap.PagingToolbar
23252 * @extends Roo.bootstrap.NavSimplebar
23253 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
23255 * Create a new PagingToolbar
23256 * @param {Object} config The config object
23257 * @param {Roo.data.Store} store
23259 Roo.bootstrap.PagingToolbar = function(config)
23261 // old args format still supported... - xtype is prefered..
23262 // created from xtype...
23264 this.ds = config.dataSource;
23266 if (config.store && !this.ds) {
23267 this.store= Roo.factory(config.store, Roo.data);
23268 this.ds = this.store;
23269 this.ds.xmodule = this.xmodule || false;
23272 this.toolbarItems = [];
23273 if (config.items) {
23274 this.toolbarItems = config.items;
23277 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
23282 this.bind(this.ds);
23285 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
23289 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
23291 * @cfg {Roo.data.Store} dataSource
23292 * The underlying data store providing the paged data
23295 * @cfg {String/HTMLElement/Element} container
23296 * container The id or element that will contain the toolbar
23299 * @cfg {Boolean} displayInfo
23300 * True to display the displayMsg (defaults to false)
23303 * @cfg {Number} pageSize
23304 * The number of records to display per page (defaults to 20)
23308 * @cfg {String} displayMsg
23309 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
23311 displayMsg : 'Displaying {0} - {1} of {2}',
23313 * @cfg {String} emptyMsg
23314 * The message to display when no records are found (defaults to "No data to display")
23316 emptyMsg : 'No data to display',
23318 * Customizable piece of the default paging text (defaults to "Page")
23321 beforePageText : "Page",
23323 * Customizable piece of the default paging text (defaults to "of %0")
23326 afterPageText : "of {0}",
23328 * Customizable piece of the default paging text (defaults to "First Page")
23331 firstText : "First Page",
23333 * Customizable piece of the default paging text (defaults to "Previous Page")
23336 prevText : "Previous Page",
23338 * Customizable piece of the default paging text (defaults to "Next Page")
23341 nextText : "Next Page",
23343 * Customizable piece of the default paging text (defaults to "Last Page")
23346 lastText : "Last Page",
23348 * Customizable piece of the default paging text (defaults to "Refresh")
23351 refreshText : "Refresh",
23355 onRender : function(ct, position)
23357 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
23358 this.navgroup.parentId = this.id;
23359 this.navgroup.onRender(this.el, null);
23360 // add the buttons to the navgroup
23362 if(this.displayInfo){
23363 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
23364 this.displayEl = this.el.select('.x-paging-info', true).first();
23365 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
23366 // this.displayEl = navel.el.select('span',true).first();
23372 Roo.each(_this.buttons, function(e){ // this might need to use render????
23373 Roo.factory(e).onRender(_this.el, null);
23377 Roo.each(_this.toolbarItems, function(e) {
23378 _this.navgroup.addItem(e);
23382 this.first = this.navgroup.addItem({
23383 tooltip: this.firstText,
23385 icon : 'fa fa-backward',
23387 preventDefault: true,
23388 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
23391 this.prev = this.navgroup.addItem({
23392 tooltip: this.prevText,
23394 icon : 'fa fa-step-backward',
23396 preventDefault: true,
23397 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
23399 //this.addSeparator();
23402 var field = this.navgroup.addItem( {
23404 cls : 'x-paging-position',
23406 html : this.beforePageText +
23407 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
23408 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
23411 this.field = field.el.select('input', true).first();
23412 this.field.on("keydown", this.onPagingKeydown, this);
23413 this.field.on("focus", function(){this.dom.select();});
23416 this.afterTextEl = field.el.select('.x-paging-after',true).first();
23417 //this.field.setHeight(18);
23418 //this.addSeparator();
23419 this.next = this.navgroup.addItem({
23420 tooltip: this.nextText,
23422 html : ' <i class="fa fa-step-forward">',
23424 preventDefault: true,
23425 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
23427 this.last = this.navgroup.addItem({
23428 tooltip: this.lastText,
23429 icon : 'fa fa-forward',
23432 preventDefault: true,
23433 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
23435 //this.addSeparator();
23436 this.loading = this.navgroup.addItem({
23437 tooltip: this.refreshText,
23438 icon: 'fa fa-refresh',
23439 preventDefault: true,
23440 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
23446 updateInfo : function(){
23447 if(this.displayEl){
23448 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
23449 var msg = count == 0 ?
23453 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
23455 this.displayEl.update(msg);
23460 onLoad : function(ds, r, o){
23461 this.cursor = o.params ? o.params.start : 0;
23462 var d = this.getPageData(),
23466 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
23467 this.field.dom.value = ap;
23468 this.first.setDisabled(ap == 1);
23469 this.prev.setDisabled(ap == 1);
23470 this.next.setDisabled(ap == ps);
23471 this.last.setDisabled(ap == ps);
23472 this.loading.enable();
23477 getPageData : function(){
23478 var total = this.ds.getTotalCount();
23481 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
23482 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
23487 onLoadError : function(){
23488 this.loading.enable();
23492 onPagingKeydown : function(e){
23493 var k = e.getKey();
23494 var d = this.getPageData();
23496 var v = this.field.dom.value, pageNum;
23497 if(!v || isNaN(pageNum = parseInt(v, 10))){
23498 this.field.dom.value = d.activePage;
23501 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
23502 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23505 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))
23507 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
23508 this.field.dom.value = pageNum;
23509 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
23512 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
23514 var v = this.field.dom.value, pageNum;
23515 var increment = (e.shiftKey) ? 10 : 1;
23516 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
23519 if(!v || isNaN(pageNum = parseInt(v, 10))) {
23520 this.field.dom.value = d.activePage;
23523 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
23525 this.field.dom.value = parseInt(v, 10) + increment;
23526 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
23527 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23534 beforeLoad : function(){
23536 this.loading.disable();
23541 onClick : function(which){
23550 ds.load({params:{start: 0, limit: this.pageSize}});
23553 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
23556 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
23559 var total = ds.getTotalCount();
23560 var extra = total % this.pageSize;
23561 var lastStart = extra ? (total - extra) : total-this.pageSize;
23562 ds.load({params:{start: lastStart, limit: this.pageSize}});
23565 ds.load({params:{start: this.cursor, limit: this.pageSize}});
23571 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
23572 * @param {Roo.data.Store} store The data store to unbind
23574 unbind : function(ds){
23575 ds.un("beforeload", this.beforeLoad, this);
23576 ds.un("load", this.onLoad, this);
23577 ds.un("loadexception", this.onLoadError, this);
23578 ds.un("remove", this.updateInfo, this);
23579 ds.un("add", this.updateInfo, this);
23580 this.ds = undefined;
23584 * Binds the paging toolbar to the specified {@link Roo.data.Store}
23585 * @param {Roo.data.Store} store The data store to bind
23587 bind : function(ds){
23588 ds.on("beforeload", this.beforeLoad, this);
23589 ds.on("load", this.onLoad, this);
23590 ds.on("loadexception", this.onLoadError, this);
23591 ds.on("remove", this.updateInfo, this);
23592 ds.on("add", this.updateInfo, this);
23603 * @class Roo.bootstrap.MessageBar
23604 * @extends Roo.bootstrap.Component
23605 * Bootstrap MessageBar class
23606 * @cfg {String} html contents of the MessageBar
23607 * @cfg {String} weight (info | success | warning | danger) default info
23608 * @cfg {String} beforeClass insert the bar before the given class
23609 * @cfg {Boolean} closable (true | false) default false
23610 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
23613 * Create a new Element
23614 * @param {Object} config The config object
23617 Roo.bootstrap.MessageBar = function(config){
23618 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
23621 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
23627 beforeClass: 'bootstrap-sticky-wrap',
23629 getAutoCreate : function(){
23633 cls: 'alert alert-dismissable alert-' + this.weight,
23638 html: this.html || ''
23644 cfg.cls += ' alert-messages-fixed';
23658 onRender : function(ct, position)
23660 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
23663 var cfg = Roo.apply({}, this.getAutoCreate());
23667 cfg.cls += ' ' + this.cls;
23670 cfg.style = this.style;
23672 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
23674 this.el.setVisibilityMode(Roo.Element.DISPLAY);
23677 this.el.select('>button.close').on('click', this.hide, this);
23683 if (!this.rendered) {
23689 this.fireEvent('show', this);
23695 if (!this.rendered) {
23701 this.fireEvent('hide', this);
23704 update : function()
23706 // var e = this.el.dom.firstChild;
23708 // if(this.closable){
23709 // e = e.nextSibling;
23712 // e.data = this.html || '';
23714 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
23730 * @class Roo.bootstrap.Graph
23731 * @extends Roo.bootstrap.Component
23732 * Bootstrap Graph class
23736 @cfg {String} graphtype bar | vbar | pie
23737 @cfg {number} g_x coodinator | centre x (pie)
23738 @cfg {number} g_y coodinator | centre y (pie)
23739 @cfg {number} g_r radius (pie)
23740 @cfg {number} g_height height of the chart (respected by all elements in the set)
23741 @cfg {number} g_width width of the chart (respected by all elements in the set)
23742 @cfg {Object} title The title of the chart
23745 -opts (object) options for the chart
23747 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
23748 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
23750 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.
23751 o stacked (boolean) whether or not to tread values as in a stacked bar chart
23753 o stretch (boolean)
23755 -opts (object) options for the pie
23758 o startAngle (number)
23759 o endAngle (number)
23763 * Create a new Input
23764 * @param {Object} config The config object
23767 Roo.bootstrap.Graph = function(config){
23768 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
23774 * The img click event for the img.
23775 * @param {Roo.EventObject} e
23781 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
23792 //g_colors: this.colors,
23799 getAutoCreate : function(){
23810 onRender : function(ct,position){
23813 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
23815 if (typeof(Raphael) == 'undefined') {
23816 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
23820 this.raphael = Raphael(this.el.dom);
23822 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23823 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23824 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23825 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
23827 r.text(160, 10, "Single Series Chart").attr(txtattr);
23828 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
23829 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
23830 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
23832 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
23833 r.barchart(330, 10, 300, 220, data1);
23834 r.barchart(10, 250, 300, 220, data2, {stacked: true});
23835 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
23838 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23839 // r.barchart(30, 30, 560, 250, xdata, {
23840 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
23841 // axis : "0 0 1 1",
23842 // axisxlabels : xdata
23843 // //yvalues : cols,
23846 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23848 // this.load(null,xdata,{
23849 // axis : "0 0 1 1",
23850 // axisxlabels : xdata
23855 load : function(graphtype,xdata,opts)
23857 this.raphael.clear();
23859 graphtype = this.graphtype;
23864 var r = this.raphael,
23865 fin = function () {
23866 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
23868 fout = function () {
23869 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
23871 pfin = function() {
23872 this.sector.stop();
23873 this.sector.scale(1.1, 1.1, this.cx, this.cy);
23876 this.label[0].stop();
23877 this.label[0].attr({ r: 7.5 });
23878 this.label[1].attr({ "font-weight": 800 });
23881 pfout = function() {
23882 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
23885 this.label[0].animate({ r: 5 }, 500, "bounce");
23886 this.label[1].attr({ "font-weight": 400 });
23892 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23895 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23898 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
23899 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
23901 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
23908 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
23913 setTitle: function(o)
23918 initEvents: function() {
23921 this.el.on('click', this.onClick, this);
23925 onClick : function(e)
23927 Roo.log('img onclick');
23928 this.fireEvent('click', this, e);
23940 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23943 * @class Roo.bootstrap.dash.NumberBox
23944 * @extends Roo.bootstrap.Component
23945 * Bootstrap NumberBox class
23946 * @cfg {String} headline Box headline
23947 * @cfg {String} content Box content
23948 * @cfg {String} icon Box icon
23949 * @cfg {String} footer Footer text
23950 * @cfg {String} fhref Footer href
23953 * Create a new NumberBox
23954 * @param {Object} config The config object
23958 Roo.bootstrap.dash.NumberBox = function(config){
23959 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
23963 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
23972 getAutoCreate : function(){
23976 cls : 'small-box ',
23984 cls : 'roo-headline',
23985 html : this.headline
23989 cls : 'roo-content',
23990 html : this.content
24004 cls : 'ion ' + this.icon
24013 cls : 'small-box-footer',
24014 href : this.fhref || '#',
24018 cfg.cn.push(footer);
24025 onRender : function(ct,position){
24026 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
24033 setHeadline: function (value)
24035 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
24038 setFooter: function (value, href)
24040 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
24043 this.el.select('a.small-box-footer',true).first().attr('href', href);
24048 setContent: function (value)
24050 this.el.select('.roo-content',true).first().dom.innerHTML = value;
24053 initEvents: function()
24067 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24070 * @class Roo.bootstrap.dash.TabBox
24071 * @extends Roo.bootstrap.Component
24072 * Bootstrap TabBox class
24073 * @cfg {String} title Title of the TabBox
24074 * @cfg {String} icon Icon of the TabBox
24075 * @cfg {Boolean} showtabs (true|false) show the tabs default true
24076 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
24079 * Create a new TabBox
24080 * @param {Object} config The config object
24084 Roo.bootstrap.dash.TabBox = function(config){
24085 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
24090 * When a pane is added
24091 * @param {Roo.bootstrap.dash.TabPane} pane
24095 * @event activatepane
24096 * When a pane is activated
24097 * @param {Roo.bootstrap.dash.TabPane} pane
24099 "activatepane" : true
24107 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
24112 tabScrollable : false,
24114 getChildContainer : function()
24116 return this.el.select('.tab-content', true).first();
24119 getAutoCreate : function(){
24123 cls: 'pull-left header',
24131 cls: 'fa ' + this.icon
24137 cls: 'nav nav-tabs pull-right',
24143 if(this.tabScrollable){
24150 cls: 'nav nav-tabs pull-right',
24161 cls: 'nav-tabs-custom',
24166 cls: 'tab-content no-padding',
24174 initEvents : function()
24176 //Roo.log('add add pane handler');
24177 this.on('addpane', this.onAddPane, this);
24180 * Updates the box title
24181 * @param {String} html to set the title to.
24183 setTitle : function(value)
24185 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
24187 onAddPane : function(pane)
24189 this.panes.push(pane);
24190 //Roo.log('addpane');
24192 // tabs are rendere left to right..
24193 if(!this.showtabs){
24197 var ctr = this.el.select('.nav-tabs', true).first();
24200 var existing = ctr.select('.nav-tab',true);
24201 var qty = existing.getCount();;
24204 var tab = ctr.createChild({
24206 cls : 'nav-tab' + (qty ? '' : ' active'),
24214 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
24217 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
24219 pane.el.addClass('active');
24224 onTabClick : function(ev,un,ob,pane)
24226 //Roo.log('tab - prev default');
24227 ev.preventDefault();
24230 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
24231 pane.tab.addClass('active');
24232 //Roo.log(pane.title);
24233 this.getChildContainer().select('.tab-pane',true).removeClass('active');
24234 // technically we should have a deactivate event.. but maybe add later.
24235 // and it should not de-activate the selected tab...
24236 this.fireEvent('activatepane', pane);
24237 pane.el.addClass('active');
24238 pane.fireEvent('activate');
24243 getActivePane : function()
24246 Roo.each(this.panes, function(p) {
24247 if(p.el.hasClass('active')){
24268 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24270 * @class Roo.bootstrap.TabPane
24271 * @extends Roo.bootstrap.Component
24272 * Bootstrap TabPane class
24273 * @cfg {Boolean} active (false | true) Default false
24274 * @cfg {String} title title of panel
24278 * Create a new TabPane
24279 * @param {Object} config The config object
24282 Roo.bootstrap.dash.TabPane = function(config){
24283 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
24289 * When a pane is activated
24290 * @param {Roo.bootstrap.dash.TabPane} pane
24297 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
24302 // the tabBox that this is attached to.
24305 getAutoCreate : function()
24313 cfg.cls += ' active';
24318 initEvents : function()
24320 //Roo.log('trigger add pane handler');
24321 this.parent().fireEvent('addpane', this)
24325 * Updates the tab title
24326 * @param {String} html to set the title to.
24328 setTitle: function(str)
24334 this.tab.select('a', true).first().dom.innerHTML = str;
24351 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24354 * @class Roo.bootstrap.menu.Menu
24355 * @extends Roo.bootstrap.Component
24356 * Bootstrap Menu class - container for Menu
24357 * @cfg {String} html Text of the menu
24358 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
24359 * @cfg {String} icon Font awesome icon
24360 * @cfg {String} pos Menu align to (top | bottom) default bottom
24364 * Create a new Menu
24365 * @param {Object} config The config object
24369 Roo.bootstrap.menu.Menu = function(config){
24370 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
24374 * @event beforeshow
24375 * Fires before this menu is displayed
24376 * @param {Roo.bootstrap.menu.Menu} this
24380 * @event beforehide
24381 * Fires before this menu is hidden
24382 * @param {Roo.bootstrap.menu.Menu} this
24387 * Fires after this menu is displayed
24388 * @param {Roo.bootstrap.menu.Menu} this
24393 * Fires after this menu is hidden
24394 * @param {Roo.bootstrap.menu.Menu} this
24399 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
24400 * @param {Roo.bootstrap.menu.Menu} this
24401 * @param {Roo.EventObject} e
24408 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
24412 weight : 'default',
24417 getChildContainer : function() {
24418 if(this.isSubMenu){
24422 return this.el.select('ul.dropdown-menu', true).first();
24425 getAutoCreate : function()
24430 cls : 'roo-menu-text',
24438 cls : 'fa ' + this.icon
24449 cls : 'dropdown-button btn btn-' + this.weight,
24454 cls : 'dropdown-toggle btn btn-' + this.weight,
24464 cls : 'dropdown-menu'
24470 if(this.pos == 'top'){
24471 cfg.cls += ' dropup';
24474 if(this.isSubMenu){
24477 cls : 'dropdown-menu'
24484 onRender : function(ct, position)
24486 this.isSubMenu = ct.hasClass('dropdown-submenu');
24488 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
24491 initEvents : function()
24493 if(this.isSubMenu){
24497 this.hidden = true;
24499 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
24500 this.triggerEl.on('click', this.onTriggerPress, this);
24502 this.buttonEl = this.el.select('button.dropdown-button', true).first();
24503 this.buttonEl.on('click', this.onClick, this);
24509 if(this.isSubMenu){
24513 return this.el.select('ul.dropdown-menu', true).first();
24516 onClick : function(e)
24518 this.fireEvent("click", this, e);
24521 onTriggerPress : function(e)
24523 if (this.isVisible()) {
24530 isVisible : function(){
24531 return !this.hidden;
24536 this.fireEvent("beforeshow", this);
24538 this.hidden = false;
24539 this.el.addClass('open');
24541 Roo.get(document).on("mouseup", this.onMouseUp, this);
24543 this.fireEvent("show", this);
24550 this.fireEvent("beforehide", this);
24552 this.hidden = true;
24553 this.el.removeClass('open');
24555 Roo.get(document).un("mouseup", this.onMouseUp);
24557 this.fireEvent("hide", this);
24560 onMouseUp : function()
24574 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24577 * @class Roo.bootstrap.menu.Item
24578 * @extends Roo.bootstrap.Component
24579 * Bootstrap MenuItem class
24580 * @cfg {Boolean} submenu (true | false) default false
24581 * @cfg {String} html text of the item
24582 * @cfg {String} href the link
24583 * @cfg {Boolean} disable (true | false) default false
24584 * @cfg {Boolean} preventDefault (true | false) default true
24585 * @cfg {String} icon Font awesome icon
24586 * @cfg {String} pos Submenu align to (left | right) default right
24590 * Create a new Item
24591 * @param {Object} config The config object
24595 Roo.bootstrap.menu.Item = function(config){
24596 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
24600 * Fires when the mouse is hovering over this menu
24601 * @param {Roo.bootstrap.menu.Item} this
24602 * @param {Roo.EventObject} e
24607 * Fires when the mouse exits this menu
24608 * @param {Roo.bootstrap.menu.Item} this
24609 * @param {Roo.EventObject} e
24615 * The raw click event for the entire grid.
24616 * @param {Roo.EventObject} e
24622 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
24627 preventDefault: true,
24632 getAutoCreate : function()
24637 cls : 'roo-menu-item-text',
24645 cls : 'fa ' + this.icon
24654 href : this.href || '#',
24661 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
24665 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
24667 if(this.pos == 'left'){
24668 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
24675 initEvents : function()
24677 this.el.on('mouseover', this.onMouseOver, this);
24678 this.el.on('mouseout', this.onMouseOut, this);
24680 this.el.select('a', true).first().on('click', this.onClick, this);
24684 onClick : function(e)
24686 if(this.preventDefault){
24687 e.preventDefault();
24690 this.fireEvent("click", this, e);
24693 onMouseOver : function(e)
24695 if(this.submenu && this.pos == 'left'){
24696 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
24699 this.fireEvent("mouseover", this, e);
24702 onMouseOut : function(e)
24704 this.fireEvent("mouseout", this, e);
24716 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24719 * @class Roo.bootstrap.menu.Separator
24720 * @extends Roo.bootstrap.Component
24721 * Bootstrap Separator class
24724 * Create a new Separator
24725 * @param {Object} config The config object
24729 Roo.bootstrap.menu.Separator = function(config){
24730 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
24733 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
24735 getAutoCreate : function(){
24756 * @class Roo.bootstrap.Tooltip
24757 * Bootstrap Tooltip class
24758 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
24759 * to determine which dom element triggers the tooltip.
24761 * It needs to add support for additional attributes like tooltip-position
24764 * Create a new Toolti
24765 * @param {Object} config The config object
24768 Roo.bootstrap.Tooltip = function(config){
24769 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
24772 Roo.apply(Roo.bootstrap.Tooltip, {
24774 * @function init initialize tooltip monitoring.
24778 currentTip : false,
24779 currentRegion : false,
24785 Roo.get(document).on('mouseover', this.enter ,this);
24786 Roo.get(document).on('mouseout', this.leave, this);
24789 this.currentTip = new Roo.bootstrap.Tooltip();
24792 enter : function(ev)
24794 var dom = ev.getTarget();
24796 //Roo.log(['enter',dom]);
24797 var el = Roo.fly(dom);
24798 if (this.currentEl) {
24800 //Roo.log(this.currentEl);
24801 //Roo.log(this.currentEl.contains(dom));
24802 if (this.currentEl == el) {
24805 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
24811 if (this.currentTip.el) {
24812 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
24816 if(!el || el.dom == document){
24822 // you can not look for children, as if el is the body.. then everythign is the child..
24823 if (!el.attr('tooltip')) { //
24824 if (!el.select("[tooltip]").elements.length) {
24827 // is the mouse over this child...?
24828 bindEl = el.select("[tooltip]").first();
24829 var xy = ev.getXY();
24830 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
24831 //Roo.log("not in region.");
24834 //Roo.log("child element over..");
24837 this.currentEl = bindEl;
24838 this.currentTip.bind(bindEl);
24839 this.currentRegion = Roo.lib.Region.getRegion(dom);
24840 this.currentTip.enter();
24843 leave : function(ev)
24845 var dom = ev.getTarget();
24846 //Roo.log(['leave',dom]);
24847 if (!this.currentEl) {
24852 if (dom != this.currentEl.dom) {
24855 var xy = ev.getXY();
24856 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
24859 // only activate leave if mouse cursor is outside... bounding box..
24864 if (this.currentTip) {
24865 this.currentTip.leave();
24867 //Roo.log('clear currentEl');
24868 this.currentEl = false;
24873 'left' : ['r-l', [-2,0], 'right'],
24874 'right' : ['l-r', [2,0], 'left'],
24875 'bottom' : ['t-b', [0,2], 'top'],
24876 'top' : [ 'b-t', [0,-2], 'bottom']
24882 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
24887 delay : null, // can be { show : 300 , hide: 500}
24891 hoverState : null, //???
24893 placement : 'bottom',
24895 getAutoCreate : function(){
24902 cls : 'tooltip-arrow'
24905 cls : 'tooltip-inner'
24912 bind : function(el)
24918 enter : function () {
24920 if (this.timeout != null) {
24921 clearTimeout(this.timeout);
24924 this.hoverState = 'in';
24925 //Roo.log("enter - show");
24926 if (!this.delay || !this.delay.show) {
24931 this.timeout = setTimeout(function () {
24932 if (_t.hoverState == 'in') {
24935 }, this.delay.show);
24939 clearTimeout(this.timeout);
24941 this.hoverState = 'out';
24942 if (!this.delay || !this.delay.hide) {
24948 this.timeout = setTimeout(function () {
24949 //Roo.log("leave - timeout");
24951 if (_t.hoverState == 'out') {
24953 Roo.bootstrap.Tooltip.currentEl = false;
24961 this.render(document.body);
24964 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
24966 var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
24968 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
24970 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
24972 var placement = typeof this.placement == 'function' ?
24973 this.placement.call(this, this.el, on_el) :
24976 var autoToken = /\s?auto?\s?/i;
24977 var autoPlace = autoToken.test(placement);
24979 placement = placement.replace(autoToken, '') || 'top';
24983 //this.el.setXY([0,0]);
24985 //this.el.dom.style.display='block';
24987 //this.el.appendTo(on_el);
24989 var p = this.getPosition();
24990 var box = this.el.getBox();
24996 var align = Roo.bootstrap.Tooltip.alignment[placement];
24998 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
25000 if(placement == 'top' || placement == 'bottom'){
25002 placement = 'right';
25005 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
25006 placement = 'left';
25009 var scroll = Roo.select('body', true).first().getScroll();
25011 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
25017 align = Roo.bootstrap.Tooltip.alignment[placement];
25019 this.el.alignTo(this.bindEl, align[0],align[1]);
25020 //var arrow = this.el.select('.arrow',true).first();
25021 //arrow.set(align[2],
25023 this.el.addClass(placement);
25025 this.el.addClass('in fade');
25027 this.hoverState = null;
25029 if (this.el.hasClass('fade')) {
25040 //this.el.setXY([0,0]);
25041 this.el.removeClass('in');
25057 * @class Roo.bootstrap.LocationPicker
25058 * @extends Roo.bootstrap.Component
25059 * Bootstrap LocationPicker class
25060 * @cfg {Number} latitude Position when init default 0
25061 * @cfg {Number} longitude Position when init default 0
25062 * @cfg {Number} zoom default 15
25063 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
25064 * @cfg {Boolean} mapTypeControl default false
25065 * @cfg {Boolean} disableDoubleClickZoom default false
25066 * @cfg {Boolean} scrollwheel default true
25067 * @cfg {Boolean} streetViewControl default false
25068 * @cfg {Number} radius default 0
25069 * @cfg {String} locationName
25070 * @cfg {Boolean} draggable default true
25071 * @cfg {Boolean} enableAutocomplete default false
25072 * @cfg {Boolean} enableReverseGeocode default true
25073 * @cfg {String} markerTitle
25076 * Create a new LocationPicker
25077 * @param {Object} config The config object
25081 Roo.bootstrap.LocationPicker = function(config){
25083 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
25088 * Fires when the picker initialized.
25089 * @param {Roo.bootstrap.LocationPicker} this
25090 * @param {Google Location} location
25094 * @event positionchanged
25095 * Fires when the picker position changed.
25096 * @param {Roo.bootstrap.LocationPicker} this
25097 * @param {Google Location} location
25099 positionchanged : true,
25102 * Fires when the map resize.
25103 * @param {Roo.bootstrap.LocationPicker} this
25108 * Fires when the map show.
25109 * @param {Roo.bootstrap.LocationPicker} this
25114 * Fires when the map hide.
25115 * @param {Roo.bootstrap.LocationPicker} this
25120 * Fires when click the map.
25121 * @param {Roo.bootstrap.LocationPicker} this
25122 * @param {Map event} e
25126 * @event mapRightClick
25127 * Fires when right click the map.
25128 * @param {Roo.bootstrap.LocationPicker} this
25129 * @param {Map event} e
25131 mapRightClick : true,
25133 * @event markerClick
25134 * Fires when click the marker.
25135 * @param {Roo.bootstrap.LocationPicker} this
25136 * @param {Map event} e
25138 markerClick : true,
25140 * @event markerRightClick
25141 * Fires when right click the marker.
25142 * @param {Roo.bootstrap.LocationPicker} this
25143 * @param {Map event} e
25145 markerRightClick : true,
25147 * @event OverlayViewDraw
25148 * Fires when OverlayView Draw
25149 * @param {Roo.bootstrap.LocationPicker} this
25151 OverlayViewDraw : true,
25153 * @event OverlayViewOnAdd
25154 * Fires when OverlayView Draw
25155 * @param {Roo.bootstrap.LocationPicker} this
25157 OverlayViewOnAdd : true,
25159 * @event OverlayViewOnRemove
25160 * Fires when OverlayView Draw
25161 * @param {Roo.bootstrap.LocationPicker} this
25163 OverlayViewOnRemove : true,
25165 * @event OverlayViewShow
25166 * Fires when OverlayView Draw
25167 * @param {Roo.bootstrap.LocationPicker} this
25168 * @param {Pixel} cpx
25170 OverlayViewShow : true,
25172 * @event OverlayViewHide
25173 * Fires when OverlayView Draw
25174 * @param {Roo.bootstrap.LocationPicker} this
25176 OverlayViewHide : true,
25178 * @event loadexception
25179 * Fires when load google lib failed.
25180 * @param {Roo.bootstrap.LocationPicker} this
25182 loadexception : true
25187 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
25189 gMapContext: false,
25195 mapTypeControl: false,
25196 disableDoubleClickZoom: false,
25198 streetViewControl: false,
25202 enableAutocomplete: false,
25203 enableReverseGeocode: true,
25206 getAutoCreate: function()
25211 cls: 'roo-location-picker'
25217 initEvents: function(ct, position)
25219 if(!this.el.getWidth() || this.isApplied()){
25223 this.el.setVisibilityMode(Roo.Element.DISPLAY);
25228 initial: function()
25230 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
25231 this.fireEvent('loadexception', this);
25235 if(!this.mapTypeId){
25236 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
25239 this.gMapContext = this.GMapContext();
25241 this.initOverlayView();
25243 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
25247 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
25248 _this.setPosition(_this.gMapContext.marker.position);
25251 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
25252 _this.fireEvent('mapClick', this, event);
25256 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
25257 _this.fireEvent('mapRightClick', this, event);
25261 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
25262 _this.fireEvent('markerClick', this, event);
25266 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
25267 _this.fireEvent('markerRightClick', this, event);
25271 this.setPosition(this.gMapContext.location);
25273 this.fireEvent('initial', this, this.gMapContext.location);
25276 initOverlayView: function()
25280 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
25284 _this.fireEvent('OverlayViewDraw', _this);
25289 _this.fireEvent('OverlayViewOnAdd', _this);
25292 onRemove: function()
25294 _this.fireEvent('OverlayViewOnRemove', _this);
25297 show: function(cpx)
25299 _this.fireEvent('OverlayViewShow', _this, cpx);
25304 _this.fireEvent('OverlayViewHide', _this);
25310 fromLatLngToContainerPixel: function(event)
25312 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
25315 isApplied: function()
25317 return this.getGmapContext() == false ? false : true;
25320 getGmapContext: function()
25322 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
25325 GMapContext: function()
25327 var position = new google.maps.LatLng(this.latitude, this.longitude);
25329 var _map = new google.maps.Map(this.el.dom, {
25332 mapTypeId: this.mapTypeId,
25333 mapTypeControl: this.mapTypeControl,
25334 disableDoubleClickZoom: this.disableDoubleClickZoom,
25335 scrollwheel: this.scrollwheel,
25336 streetViewControl: this.streetViewControl,
25337 locationName: this.locationName,
25338 draggable: this.draggable,
25339 enableAutocomplete: this.enableAutocomplete,
25340 enableReverseGeocode: this.enableReverseGeocode
25343 var _marker = new google.maps.Marker({
25344 position: position,
25346 title: this.markerTitle,
25347 draggable: this.draggable
25354 location: position,
25355 radius: this.radius,
25356 locationName: this.locationName,
25357 addressComponents: {
25358 formatted_address: null,
25359 addressLine1: null,
25360 addressLine2: null,
25362 streetNumber: null,
25366 stateOrProvince: null
25369 domContainer: this.el.dom,
25370 geodecoder: new google.maps.Geocoder()
25374 drawCircle: function(center, radius, options)
25376 if (this.gMapContext.circle != null) {
25377 this.gMapContext.circle.setMap(null);
25381 options = Roo.apply({}, options, {
25382 strokeColor: "#0000FF",
25383 strokeOpacity: .35,
25385 fillColor: "#0000FF",
25389 options.map = this.gMapContext.map;
25390 options.radius = radius;
25391 options.center = center;
25392 this.gMapContext.circle = new google.maps.Circle(options);
25393 return this.gMapContext.circle;
25399 setPosition: function(location)
25401 this.gMapContext.location = location;
25402 this.gMapContext.marker.setPosition(location);
25403 this.gMapContext.map.panTo(location);
25404 this.drawCircle(location, this.gMapContext.radius, {});
25408 if (this.gMapContext.settings.enableReverseGeocode) {
25409 this.gMapContext.geodecoder.geocode({
25410 latLng: this.gMapContext.location
25411 }, function(results, status) {
25413 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
25414 _this.gMapContext.locationName = results[0].formatted_address;
25415 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
25417 _this.fireEvent('positionchanged', this, location);
25424 this.fireEvent('positionchanged', this, location);
25429 google.maps.event.trigger(this.gMapContext.map, "resize");
25431 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
25433 this.fireEvent('resize', this);
25436 setPositionByLatLng: function(latitude, longitude)
25438 this.setPosition(new google.maps.LatLng(latitude, longitude));
25441 getCurrentPosition: function()
25444 latitude: this.gMapContext.location.lat(),
25445 longitude: this.gMapContext.location.lng()
25449 getAddressName: function()
25451 return this.gMapContext.locationName;
25454 getAddressComponents: function()
25456 return this.gMapContext.addressComponents;
25459 address_component_from_google_geocode: function(address_components)
25463 for (var i = 0; i < address_components.length; i++) {
25464 var component = address_components[i];
25465 if (component.types.indexOf("postal_code") >= 0) {
25466 result.postalCode = component.short_name;
25467 } else if (component.types.indexOf("street_number") >= 0) {
25468 result.streetNumber = component.short_name;
25469 } else if (component.types.indexOf("route") >= 0) {
25470 result.streetName = component.short_name;
25471 } else if (component.types.indexOf("neighborhood") >= 0) {
25472 result.city = component.short_name;
25473 } else if (component.types.indexOf("locality") >= 0) {
25474 result.city = component.short_name;
25475 } else if (component.types.indexOf("sublocality") >= 0) {
25476 result.district = component.short_name;
25477 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
25478 result.stateOrProvince = component.short_name;
25479 } else if (component.types.indexOf("country") >= 0) {
25480 result.country = component.short_name;
25484 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
25485 result.addressLine2 = "";
25489 setZoomLevel: function(zoom)
25491 this.gMapContext.map.setZoom(zoom);
25504 this.fireEvent('show', this);
25515 this.fireEvent('hide', this);
25520 Roo.apply(Roo.bootstrap.LocationPicker, {
25522 OverlayView : function(map, options)
25524 options = options || {};
25538 * @class Roo.bootstrap.Alert
25539 * @extends Roo.bootstrap.Component
25540 * Bootstrap Alert class
25541 * @cfg {String} title The title of alert
25542 * @cfg {String} html The content of alert
25543 * @cfg {String} weight ( success | info | warning | danger )
25544 * @cfg {String} faicon font-awesomeicon
25547 * Create a new alert
25548 * @param {Object} config The config object
25552 Roo.bootstrap.Alert = function(config){
25553 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
25557 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
25564 getAutoCreate : function()
25573 cls : 'roo-alert-icon'
25578 cls : 'roo-alert-title',
25583 cls : 'roo-alert-text',
25590 cfg.cn[0].cls += ' fa ' + this.faicon;
25594 cfg.cls += ' alert-' + this.weight;
25600 initEvents: function()
25602 this.el.setVisibilityMode(Roo.Element.DISPLAY);
25605 setTitle : function(str)
25607 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
25610 setText : function(str)
25612 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
25615 setWeight : function(weight)
25618 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
25621 this.weight = weight;
25623 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
25626 setIcon : function(icon)
25629 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
25632 this.faicon = icon;
25634 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
25655 * @class Roo.bootstrap.UploadCropbox
25656 * @extends Roo.bootstrap.Component
25657 * Bootstrap UploadCropbox class
25658 * @cfg {String} emptyText show when image has been loaded
25659 * @cfg {String} rotateNotify show when image too small to rotate
25660 * @cfg {Number} errorTimeout default 3000
25661 * @cfg {Number} minWidth default 300
25662 * @cfg {Number} minHeight default 300
25663 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
25664 * @cfg {Boolean} isDocument (true|false) default false
25665 * @cfg {String} url action url
25666 * @cfg {String} paramName default 'imageUpload'
25667 * @cfg {String} method default POST
25668 * @cfg {Boolean} loadMask (true|false) default true
25669 * @cfg {Boolean} loadingText default 'Loading...'
25672 * Create a new UploadCropbox
25673 * @param {Object} config The config object
25676 Roo.bootstrap.UploadCropbox = function(config){
25677 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
25681 * @event beforeselectfile
25682 * Fire before select file
25683 * @param {Roo.bootstrap.UploadCropbox} this
25685 "beforeselectfile" : true,
25688 * Fire after initEvent
25689 * @param {Roo.bootstrap.UploadCropbox} this
25694 * Fire after initEvent
25695 * @param {Roo.bootstrap.UploadCropbox} this
25696 * @param {String} data
25701 * Fire when preparing the file data
25702 * @param {Roo.bootstrap.UploadCropbox} this
25703 * @param {Object} file
25708 * Fire when get exception
25709 * @param {Roo.bootstrap.UploadCropbox} this
25710 * @param {XMLHttpRequest} xhr
25712 "exception" : true,
25714 * @event beforeloadcanvas
25715 * Fire before load the canvas
25716 * @param {Roo.bootstrap.UploadCropbox} this
25717 * @param {String} src
25719 "beforeloadcanvas" : true,
25722 * Fire when trash image
25723 * @param {Roo.bootstrap.UploadCropbox} this
25728 * Fire when download the image
25729 * @param {Roo.bootstrap.UploadCropbox} this
25733 * @event footerbuttonclick
25734 * Fire when footerbuttonclick
25735 * @param {Roo.bootstrap.UploadCropbox} this
25736 * @param {String} type
25738 "footerbuttonclick" : true,
25742 * @param {Roo.bootstrap.UploadCropbox} this
25747 * Fire when rotate the image
25748 * @param {Roo.bootstrap.UploadCropbox} this
25749 * @param {String} pos
25754 * Fire when inspect the file
25755 * @param {Roo.bootstrap.UploadCropbox} this
25756 * @param {Object} file
25761 * Fire when xhr upload the file
25762 * @param {Roo.bootstrap.UploadCropbox} this
25763 * @param {Object} data
25768 * Fire when arrange the file data
25769 * @param {Roo.bootstrap.UploadCropbox} this
25770 * @param {Object} formData
25775 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
25778 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
25780 emptyText : 'Click to upload image',
25781 rotateNotify : 'Image is too small to rotate',
25782 errorTimeout : 3000,
25796 cropType : 'image/jpeg',
25798 canvasLoaded : false,
25799 isDocument : false,
25801 paramName : 'imageUpload',
25803 loadingText : 'Loading...',
25806 getAutoCreate : function()
25810 cls : 'roo-upload-cropbox',
25814 cls : 'roo-upload-cropbox-selector',
25819 cls : 'roo-upload-cropbox-body',
25820 style : 'cursor:pointer',
25824 cls : 'roo-upload-cropbox-preview'
25828 cls : 'roo-upload-cropbox-thumb'
25832 cls : 'roo-upload-cropbox-empty-notify',
25833 html : this.emptyText
25837 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
25838 html : this.rotateNotify
25844 cls : 'roo-upload-cropbox-footer',
25847 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
25857 onRender : function(ct, position)
25859 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
25861 if (this.buttons.length) {
25863 Roo.each(this.buttons, function(bb) {
25865 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
25867 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
25873 this.maskEl = this.el;
25877 initEvents : function()
25879 this.urlAPI = (window.createObjectURL && window) ||
25880 (window.URL && URL.revokeObjectURL && URL) ||
25881 (window.webkitURL && webkitURL);
25883 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
25884 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25886 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
25887 this.selectorEl.hide();
25889 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
25890 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25892 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
25893 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25894 this.thumbEl.hide();
25896 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
25897 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25899 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
25900 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25901 this.errorEl.hide();
25903 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
25904 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25905 this.footerEl.hide();
25907 this.setThumbBoxSize();
25913 this.fireEvent('initial', this);
25920 window.addEventListener("resize", function() { _this.resize(); } );
25922 this.bodyEl.on('click', this.beforeSelectFile, this);
25925 this.bodyEl.on('touchstart', this.onTouchStart, this);
25926 this.bodyEl.on('touchmove', this.onTouchMove, this);
25927 this.bodyEl.on('touchend', this.onTouchEnd, this);
25931 this.bodyEl.on('mousedown', this.onMouseDown, this);
25932 this.bodyEl.on('mousemove', this.onMouseMove, this);
25933 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
25934 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
25935 Roo.get(document).on('mouseup', this.onMouseUp, this);
25938 this.selectorEl.on('change', this.onFileSelected, this);
25944 this.baseScale = 1;
25946 this.baseRotate = 1;
25947 this.dragable = false;
25948 this.pinching = false;
25951 this.cropData = false;
25952 this.notifyEl.dom.innerHTML = this.emptyText;
25954 this.selectorEl.dom.value = '';
25958 resize : function()
25960 if(this.fireEvent('resize', this) != false){
25961 this.setThumbBoxPosition();
25962 this.setCanvasPosition();
25966 onFooterButtonClick : function(e, el, o, type)
25969 case 'rotate-left' :
25970 this.onRotateLeft(e);
25972 case 'rotate-right' :
25973 this.onRotateRight(e);
25976 this.beforeSelectFile(e);
25991 this.fireEvent('footerbuttonclick', this, type);
25994 beforeSelectFile : function(e)
25996 e.preventDefault();
25998 if(this.fireEvent('beforeselectfile', this) != false){
25999 this.selectorEl.dom.click();
26003 onFileSelected : function(e)
26005 e.preventDefault();
26007 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26011 var file = this.selectorEl.dom.files[0];
26013 if(this.fireEvent('inspect', this, file) != false){
26014 this.prepare(file);
26019 trash : function(e)
26021 this.fireEvent('trash', this);
26024 download : function(e)
26026 this.fireEvent('download', this);
26029 loadCanvas : function(src)
26031 if(this.fireEvent('beforeloadcanvas', this, src) != false){
26035 this.imageEl = document.createElement('img');
26039 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
26041 this.imageEl.src = src;
26045 onLoadCanvas : function()
26047 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
26048 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
26050 this.bodyEl.un('click', this.beforeSelectFile, this);
26052 this.notifyEl.hide();
26053 this.thumbEl.show();
26054 this.footerEl.show();
26056 this.baseRotateLevel();
26058 if(this.isDocument){
26059 this.setThumbBoxSize();
26062 this.setThumbBoxPosition();
26064 this.baseScaleLevel();
26070 this.canvasLoaded = true;
26073 this.maskEl.unmask();
26078 setCanvasPosition : function()
26080 if(!this.canvasEl){
26084 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
26085 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
26087 this.previewEl.setLeft(pw);
26088 this.previewEl.setTop(ph);
26092 onMouseDown : function(e)
26096 this.dragable = true;
26097 this.pinching = false;
26099 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
26100 this.dragable = false;
26104 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26105 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26109 onMouseMove : function(e)
26113 if(!this.canvasLoaded){
26117 if (!this.dragable){
26121 var minX = Math.ceil(this.thumbEl.getLeft(true));
26122 var minY = Math.ceil(this.thumbEl.getTop(true));
26124 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
26125 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
26127 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26128 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26130 x = x - this.mouseX;
26131 y = y - this.mouseY;
26133 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
26134 var bgY = Math.ceil(y + this.previewEl.getTop(true));
26136 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
26137 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
26139 this.previewEl.setLeft(bgX);
26140 this.previewEl.setTop(bgY);
26142 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26143 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26146 onMouseUp : function(e)
26150 this.dragable = false;
26153 onMouseWheel : function(e)
26157 this.startScale = this.scale;
26159 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
26161 if(!this.zoomable()){
26162 this.scale = this.startScale;
26171 zoomable : function()
26173 var minScale = this.thumbEl.getWidth() / this.minWidth;
26175 if(this.minWidth < this.minHeight){
26176 minScale = this.thumbEl.getHeight() / this.minHeight;
26179 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
26180 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
26184 (this.rotate == 0 || this.rotate == 180) &&
26186 width > this.imageEl.OriginWidth ||
26187 height > this.imageEl.OriginHeight ||
26188 (width < this.minWidth && height < this.minHeight)
26196 (this.rotate == 90 || this.rotate == 270) &&
26198 width > this.imageEl.OriginWidth ||
26199 height > this.imageEl.OriginHeight ||
26200 (width < this.minHeight && height < this.minWidth)
26207 !this.isDocument &&
26208 (this.rotate == 0 || this.rotate == 180) &&
26210 width < this.minWidth ||
26211 width > this.imageEl.OriginWidth ||
26212 height < this.minHeight ||
26213 height > this.imageEl.OriginHeight
26220 !this.isDocument &&
26221 (this.rotate == 90 || this.rotate == 270) &&
26223 width < this.minHeight ||
26224 width > this.imageEl.OriginWidth ||
26225 height < this.minWidth ||
26226 height > this.imageEl.OriginHeight
26236 onRotateLeft : function(e)
26238 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
26240 var minScale = this.thumbEl.getWidth() / this.minWidth;
26242 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
26243 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
26245 this.startScale = this.scale;
26247 while (this.getScaleLevel() < minScale){
26249 this.scale = this.scale + 1;
26251 if(!this.zoomable()){
26256 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
26257 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
26262 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
26269 this.scale = this.startScale;
26271 this.onRotateFail();
26276 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
26278 if(this.isDocument){
26279 this.setThumbBoxSize();
26280 this.setThumbBoxPosition();
26281 this.setCanvasPosition();
26286 this.fireEvent('rotate', this, 'left');
26290 onRotateRight : function(e)
26292 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
26294 var minScale = this.thumbEl.getWidth() / this.minWidth;
26296 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
26297 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
26299 this.startScale = this.scale;
26301 while (this.getScaleLevel() < minScale){
26303 this.scale = this.scale + 1;
26305 if(!this.zoomable()){
26310 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
26311 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
26316 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
26323 this.scale = this.startScale;
26325 this.onRotateFail();
26330 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
26332 if(this.isDocument){
26333 this.setThumbBoxSize();
26334 this.setThumbBoxPosition();
26335 this.setCanvasPosition();
26340 this.fireEvent('rotate', this, 'right');
26343 onRotateFail : function()
26345 this.errorEl.show(true);
26349 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
26354 this.previewEl.dom.innerHTML = '';
26356 var canvasEl = document.createElement("canvas");
26358 var contextEl = canvasEl.getContext("2d");
26360 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26361 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26362 var center = this.imageEl.OriginWidth / 2;
26364 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
26365 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26366 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26367 center = this.imageEl.OriginHeight / 2;
26370 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
26372 contextEl.translate(center, center);
26373 contextEl.rotate(this.rotate * Math.PI / 180);
26375 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26377 this.canvasEl = document.createElement("canvas");
26379 this.contextEl = this.canvasEl.getContext("2d");
26381 switch (this.rotate) {
26384 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26385 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26387 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26392 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26393 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26395 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26396 this.contextEl.drawImage(canvasEl, Math.abs(this.canvasEl.width - this.canvasEl.height), 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26400 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26405 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26406 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26408 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26409 this.contextEl.drawImage(canvasEl, 0, Math.abs(this.canvasEl.width - this.canvasEl.height), this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26413 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);
26418 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26419 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26421 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26422 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26426 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);
26433 this.previewEl.appendChild(this.canvasEl);
26435 this.setCanvasPosition();
26440 if(!this.canvasLoaded){
26444 var imageCanvas = document.createElement("canvas");
26446 var imageContext = imageCanvas.getContext("2d");
26448 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26449 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26451 var center = imageCanvas.width / 2;
26453 imageContext.translate(center, center);
26455 imageContext.rotate(this.rotate * Math.PI / 180);
26457 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26459 var canvas = document.createElement("canvas");
26461 var context = canvas.getContext("2d");
26463 canvas.width = this.minWidth;
26464 canvas.height = this.minHeight;
26466 switch (this.rotate) {
26469 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26470 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26472 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26473 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26475 var targetWidth = this.minWidth - 2 * x;
26476 var targetHeight = this.minHeight - 2 * y;
26480 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26481 scale = targetWidth / width;
26484 if(x > 0 && y == 0){
26485 scale = targetHeight / height;
26488 if(x > 0 && y > 0){
26489 scale = targetWidth / width;
26491 if(width < height){
26492 scale = targetHeight / height;
26496 context.scale(scale, scale);
26498 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26499 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26501 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26502 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26504 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26509 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26510 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26512 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26513 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26515 var targetWidth = this.minWidth - 2 * x;
26516 var targetHeight = this.minHeight - 2 * y;
26520 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26521 scale = targetWidth / width;
26524 if(x > 0 && y == 0){
26525 scale = targetHeight / height;
26528 if(x > 0 && y > 0){
26529 scale = targetWidth / width;
26531 if(width < height){
26532 scale = targetHeight / height;
26536 context.scale(scale, scale);
26538 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26539 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26541 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26542 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26544 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26546 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26551 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26552 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26554 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26555 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26557 var targetWidth = this.minWidth - 2 * x;
26558 var targetHeight = this.minHeight - 2 * y;
26562 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26563 scale = targetWidth / width;
26566 if(x > 0 && y == 0){
26567 scale = targetHeight / height;
26570 if(x > 0 && y > 0){
26571 scale = targetWidth / width;
26573 if(width < height){
26574 scale = targetHeight / height;
26578 context.scale(scale, scale);
26580 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26581 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26583 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26584 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26586 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26587 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26589 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26594 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26595 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26597 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26598 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26600 var targetWidth = this.minWidth - 2 * x;
26601 var targetHeight = this.minHeight - 2 * y;
26605 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26606 scale = targetWidth / width;
26609 if(x > 0 && y == 0){
26610 scale = targetHeight / height;
26613 if(x > 0 && y > 0){
26614 scale = targetWidth / width;
26616 if(width < height){
26617 scale = targetHeight / height;
26621 context.scale(scale, scale);
26623 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26624 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26626 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26627 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26629 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26631 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26638 this.cropData = canvas.toDataURL(this.cropType);
26640 if(this.fireEvent('crop', this, this.cropData) !== false){
26641 this.process(this.file, this.cropData);
26648 setThumbBoxSize : function()
26652 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
26653 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
26654 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
26656 this.minWidth = width;
26657 this.minHeight = height;
26659 if(this.rotate == 90 || this.rotate == 270){
26660 this.minWidth = height;
26661 this.minHeight = width;
26666 width = Math.ceil(this.minWidth * height / this.minHeight);
26668 if(this.minWidth > this.minHeight){
26670 height = Math.ceil(this.minHeight * width / this.minWidth);
26673 this.thumbEl.setStyle({
26674 width : width + 'px',
26675 height : height + 'px'
26682 setThumbBoxPosition : function()
26684 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
26685 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
26687 this.thumbEl.setLeft(x);
26688 this.thumbEl.setTop(y);
26692 baseRotateLevel : function()
26694 this.baseRotate = 1;
26697 typeof(this.exif) != 'undefined' &&
26698 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
26699 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
26701 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
26704 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
26708 baseScaleLevel : function()
26712 if(this.isDocument){
26714 if(this.baseRotate == 6 || this.baseRotate == 8){
26716 height = this.thumbEl.getHeight();
26717 this.baseScale = height / this.imageEl.OriginWidth;
26719 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
26720 width = this.thumbEl.getWidth();
26721 this.baseScale = width / this.imageEl.OriginHeight;
26727 height = this.thumbEl.getHeight();
26728 this.baseScale = height / this.imageEl.OriginHeight;
26730 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
26731 width = this.thumbEl.getWidth();
26732 this.baseScale = width / this.imageEl.OriginWidth;
26738 if(this.baseRotate == 6 || this.baseRotate == 8){
26740 width = this.thumbEl.getHeight();
26741 this.baseScale = width / this.imageEl.OriginHeight;
26743 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
26744 height = this.thumbEl.getWidth();
26745 this.baseScale = height / this.imageEl.OriginHeight;
26748 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26749 height = this.thumbEl.getWidth();
26750 this.baseScale = height / this.imageEl.OriginHeight;
26752 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
26753 width = this.thumbEl.getHeight();
26754 this.baseScale = width / this.imageEl.OriginWidth;
26761 width = this.thumbEl.getWidth();
26762 this.baseScale = width / this.imageEl.OriginWidth;
26764 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
26765 height = this.thumbEl.getHeight();
26766 this.baseScale = height / this.imageEl.OriginHeight;
26769 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26771 height = this.thumbEl.getHeight();
26772 this.baseScale = height / this.imageEl.OriginHeight;
26774 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
26775 width = this.thumbEl.getWidth();
26776 this.baseScale = width / this.imageEl.OriginWidth;
26784 getScaleLevel : function()
26786 return this.baseScale * Math.pow(1.1, this.scale);
26789 onTouchStart : function(e)
26791 if(!this.canvasLoaded){
26792 this.beforeSelectFile(e);
26796 var touches = e.browserEvent.touches;
26802 if(touches.length == 1){
26803 this.onMouseDown(e);
26807 if(touches.length != 2){
26813 for(var i = 0, finger; finger = touches[i]; i++){
26814 coords.push(finger.pageX, finger.pageY);
26817 var x = Math.pow(coords[0] - coords[2], 2);
26818 var y = Math.pow(coords[1] - coords[3], 2);
26820 this.startDistance = Math.sqrt(x + y);
26822 this.startScale = this.scale;
26824 this.pinching = true;
26825 this.dragable = false;
26829 onTouchMove : function(e)
26831 if(!this.pinching && !this.dragable){
26835 var touches = e.browserEvent.touches;
26842 this.onMouseMove(e);
26848 for(var i = 0, finger; finger = touches[i]; i++){
26849 coords.push(finger.pageX, finger.pageY);
26852 var x = Math.pow(coords[0] - coords[2], 2);
26853 var y = Math.pow(coords[1] - coords[3], 2);
26855 this.endDistance = Math.sqrt(x + y);
26857 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
26859 if(!this.zoomable()){
26860 this.scale = this.startScale;
26868 onTouchEnd : function(e)
26870 this.pinching = false;
26871 this.dragable = false;
26875 process : function(file, crop)
26878 this.maskEl.mask(this.loadingText);
26881 this.xhr = new XMLHttpRequest();
26883 file.xhr = this.xhr;
26885 this.xhr.open(this.method, this.url, true);
26888 "Accept": "application/json",
26889 "Cache-Control": "no-cache",
26890 "X-Requested-With": "XMLHttpRequest"
26893 for (var headerName in headers) {
26894 var headerValue = headers[headerName];
26896 this.xhr.setRequestHeader(headerName, headerValue);
26902 this.xhr.onload = function()
26904 _this.xhrOnLoad(_this.xhr);
26907 this.xhr.onerror = function()
26909 _this.xhrOnError(_this.xhr);
26912 var formData = new FormData();
26914 formData.append('returnHTML', 'NO');
26917 formData.append('crop', crop);
26920 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
26921 formData.append(this.paramName, file, file.name);
26924 if(typeof(file.filename) != 'undefined'){
26925 formData.append('filename', file.filename);
26928 if(typeof(file.mimetype) != 'undefined'){
26929 formData.append('mimetype', file.mimetype);
26932 if(this.fireEvent('arrange', this, formData) != false){
26933 this.xhr.send(formData);
26937 xhrOnLoad : function(xhr)
26940 this.maskEl.unmask();
26943 if (xhr.readyState !== 4) {
26944 this.fireEvent('exception', this, xhr);
26948 var response = Roo.decode(xhr.responseText);
26950 if(!response.success){
26951 this.fireEvent('exception', this, xhr);
26955 var response = Roo.decode(xhr.responseText);
26957 this.fireEvent('upload', this, response);
26961 xhrOnError : function()
26964 this.maskEl.unmask();
26967 Roo.log('xhr on error');
26969 var response = Roo.decode(xhr.responseText);
26975 prepare : function(file)
26978 this.maskEl.mask(this.loadingText);
26984 if(typeof(file) === 'string'){
26985 this.loadCanvas(file);
26989 if(!file || !this.urlAPI){
26994 this.cropType = file.type;
26998 if(this.fireEvent('prepare', this, this.file) != false){
27000 var reader = new FileReader();
27002 reader.onload = function (e) {
27003 if (e.target.error) {
27004 Roo.log(e.target.error);
27008 var buffer = e.target.result,
27009 dataView = new DataView(buffer),
27011 maxOffset = dataView.byteLength - 4,
27015 if (dataView.getUint16(0) === 0xffd8) {
27016 while (offset < maxOffset) {
27017 markerBytes = dataView.getUint16(offset);
27019 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
27020 markerLength = dataView.getUint16(offset + 2) + 2;
27021 if (offset + markerLength > dataView.byteLength) {
27022 Roo.log('Invalid meta data: Invalid segment size.');
27026 if(markerBytes == 0xffe1){
27027 _this.parseExifData(
27034 offset += markerLength;
27044 var url = _this.urlAPI.createObjectURL(_this.file);
27046 _this.loadCanvas(url);
27051 reader.readAsArrayBuffer(this.file);
27057 parseExifData : function(dataView, offset, length)
27059 var tiffOffset = offset + 10,
27063 if (dataView.getUint32(offset + 4) !== 0x45786966) {
27064 // No Exif data, might be XMP data instead
27068 // Check for the ASCII code for "Exif" (0x45786966):
27069 if (dataView.getUint32(offset + 4) !== 0x45786966) {
27070 // No Exif data, might be XMP data instead
27073 if (tiffOffset + 8 > dataView.byteLength) {
27074 Roo.log('Invalid Exif data: Invalid segment size.');
27077 // Check for the two null bytes:
27078 if (dataView.getUint16(offset + 8) !== 0x0000) {
27079 Roo.log('Invalid Exif data: Missing byte alignment offset.');
27082 // Check the byte alignment:
27083 switch (dataView.getUint16(tiffOffset)) {
27085 littleEndian = true;
27088 littleEndian = false;
27091 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
27094 // Check for the TIFF tag marker (0x002A):
27095 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
27096 Roo.log('Invalid Exif data: Missing TIFF marker.');
27099 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
27100 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
27102 this.parseExifTags(
27105 tiffOffset + dirOffset,
27110 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
27115 if (dirOffset + 6 > dataView.byteLength) {
27116 Roo.log('Invalid Exif data: Invalid directory offset.');
27119 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
27120 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
27121 if (dirEndOffset + 4 > dataView.byteLength) {
27122 Roo.log('Invalid Exif data: Invalid directory size.');
27125 for (i = 0; i < tagsNumber; i += 1) {
27129 dirOffset + 2 + 12 * i, // tag offset
27133 // Return the offset to the next directory:
27134 return dataView.getUint32(dirEndOffset, littleEndian);
27137 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
27139 var tag = dataView.getUint16(offset, littleEndian);
27141 this.exif[tag] = this.getExifValue(
27145 dataView.getUint16(offset + 2, littleEndian), // tag type
27146 dataView.getUint32(offset + 4, littleEndian), // tag length
27151 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
27153 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
27162 Roo.log('Invalid Exif data: Invalid tag type.');
27166 tagSize = tagType.size * length;
27167 // Determine if the value is contained in the dataOffset bytes,
27168 // or if the value at the dataOffset is a pointer to the actual data:
27169 dataOffset = tagSize > 4 ?
27170 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
27171 if (dataOffset + tagSize > dataView.byteLength) {
27172 Roo.log('Invalid Exif data: Invalid data offset.');
27175 if (length === 1) {
27176 return tagType.getValue(dataView, dataOffset, littleEndian);
27179 for (i = 0; i < length; i += 1) {
27180 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
27183 if (tagType.ascii) {
27185 // Concatenate the chars:
27186 for (i = 0; i < values.length; i += 1) {
27188 // Ignore the terminating NULL byte(s):
27189 if (c === '\u0000') {
27201 Roo.apply(Roo.bootstrap.UploadCropbox, {
27203 'Orientation': 0x0112
27207 1: 0, //'top-left',
27209 3: 180, //'bottom-right',
27210 // 4: 'bottom-left',
27212 6: 90, //'right-top',
27213 // 7: 'right-bottom',
27214 8: 270 //'left-bottom'
27218 // byte, 8-bit unsigned int:
27220 getValue: function (dataView, dataOffset) {
27221 return dataView.getUint8(dataOffset);
27225 // ascii, 8-bit byte:
27227 getValue: function (dataView, dataOffset) {
27228 return String.fromCharCode(dataView.getUint8(dataOffset));
27233 // short, 16 bit int:
27235 getValue: function (dataView, dataOffset, littleEndian) {
27236 return dataView.getUint16(dataOffset, littleEndian);
27240 // long, 32 bit int:
27242 getValue: function (dataView, dataOffset, littleEndian) {
27243 return dataView.getUint32(dataOffset, littleEndian);
27247 // rational = two long values, first is numerator, second is denominator:
27249 getValue: function (dataView, dataOffset, littleEndian) {
27250 return dataView.getUint32(dataOffset, littleEndian) /
27251 dataView.getUint32(dataOffset + 4, littleEndian);
27255 // slong, 32 bit signed int:
27257 getValue: function (dataView, dataOffset, littleEndian) {
27258 return dataView.getInt32(dataOffset, littleEndian);
27262 // srational, two slongs, first is numerator, second is denominator:
27264 getValue: function (dataView, dataOffset, littleEndian) {
27265 return dataView.getInt32(dataOffset, littleEndian) /
27266 dataView.getInt32(dataOffset + 4, littleEndian);
27276 cls : 'btn-group roo-upload-cropbox-rotate-left',
27277 action : 'rotate-left',
27281 cls : 'btn btn-default',
27282 html : '<i class="fa fa-undo"></i>'
27288 cls : 'btn-group roo-upload-cropbox-picture',
27289 action : 'picture',
27293 cls : 'btn btn-default',
27294 html : '<i class="fa fa-picture-o"></i>'
27300 cls : 'btn-group roo-upload-cropbox-rotate-right',
27301 action : 'rotate-right',
27305 cls : 'btn btn-default',
27306 html : '<i class="fa fa-repeat"></i>'
27314 cls : 'btn-group roo-upload-cropbox-rotate-left',
27315 action : 'rotate-left',
27319 cls : 'btn btn-default',
27320 html : '<i class="fa fa-undo"></i>'
27326 cls : 'btn-group roo-upload-cropbox-download',
27327 action : 'download',
27331 cls : 'btn btn-default',
27332 html : '<i class="fa fa-download"></i>'
27338 cls : 'btn-group roo-upload-cropbox-crop',
27343 cls : 'btn btn-default',
27344 html : '<i class="fa fa-crop"></i>'
27350 cls : 'btn-group roo-upload-cropbox-trash',
27355 cls : 'btn btn-default',
27356 html : '<i class="fa fa-trash"></i>'
27362 cls : 'btn-group roo-upload-cropbox-rotate-right',
27363 action : 'rotate-right',
27367 cls : 'btn btn-default',
27368 html : '<i class="fa fa-repeat"></i>'
27376 cls : 'btn-group roo-upload-cropbox-rotate-left',
27377 action : 'rotate-left',
27381 cls : 'btn btn-default',
27382 html : '<i class="fa fa-undo"></i>'
27388 cls : 'btn-group roo-upload-cropbox-rotate-right',
27389 action : 'rotate-right',
27393 cls : 'btn btn-default',
27394 html : '<i class="fa fa-repeat"></i>'
27407 * @class Roo.bootstrap.DocumentManager
27408 * @extends Roo.bootstrap.Component
27409 * Bootstrap DocumentManager class
27410 * @cfg {String} paramName default 'imageUpload'
27411 * @cfg {String} toolTipName default 'filename'
27412 * @cfg {String} method default POST
27413 * @cfg {String} url action url
27414 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
27415 * @cfg {Boolean} multiple multiple upload default true
27416 * @cfg {Number} thumbSize default 300
27417 * @cfg {String} fieldLabel
27418 * @cfg {Number} labelWidth default 4
27419 * @cfg {String} labelAlign (left|top) default left
27420 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
27423 * Create a new DocumentManager
27424 * @param {Object} config The config object
27427 Roo.bootstrap.DocumentManager = function(config){
27428 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
27431 this.delegates = [];
27436 * Fire when initial the DocumentManager
27437 * @param {Roo.bootstrap.DocumentManager} this
27442 * inspect selected file
27443 * @param {Roo.bootstrap.DocumentManager} this
27444 * @param {File} file
27449 * Fire when xhr load exception
27450 * @param {Roo.bootstrap.DocumentManager} this
27451 * @param {XMLHttpRequest} xhr
27453 "exception" : true,
27455 * @event afterupload
27456 * Fire when xhr load exception
27457 * @param {Roo.bootstrap.DocumentManager} this
27458 * @param {XMLHttpRequest} xhr
27460 "afterupload" : true,
27463 * prepare the form data
27464 * @param {Roo.bootstrap.DocumentManager} this
27465 * @param {Object} formData
27470 * Fire when remove the file
27471 * @param {Roo.bootstrap.DocumentManager} this
27472 * @param {Object} file
27477 * Fire after refresh the file
27478 * @param {Roo.bootstrap.DocumentManager} this
27483 * Fire after click the image
27484 * @param {Roo.bootstrap.DocumentManager} this
27485 * @param {Object} file
27490 * Fire when upload a image and editable set to true
27491 * @param {Roo.bootstrap.DocumentManager} this
27492 * @param {Object} file
27496 * @event beforeselectfile
27497 * Fire before select file
27498 * @param {Roo.bootstrap.DocumentManager} this
27500 "beforeselectfile" : true,
27503 * Fire before process file
27504 * @param {Roo.bootstrap.DocumentManager} this
27505 * @param {Object} file
27512 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
27521 paramName : 'imageUpload',
27522 toolTipName : 'filename',
27525 labelAlign : 'left',
27530 getAutoCreate : function()
27532 var managerWidget = {
27534 cls : 'roo-document-manager',
27538 cls : 'roo-document-manager-selector',
27543 cls : 'roo-document-manager-uploader',
27547 cls : 'roo-document-manager-upload-btn',
27548 html : '<i class="fa fa-plus"></i>'
27559 cls : 'column col-md-12',
27564 if(this.fieldLabel.length){
27569 cls : 'column col-md-12',
27570 html : this.fieldLabel
27574 cls : 'column col-md-12',
27579 if(this.labelAlign == 'left'){
27583 cls : 'column col-md-' + this.labelWidth,
27584 html : this.fieldLabel
27588 cls : 'column col-md-' + (12 - this.labelWidth),
27598 cls : 'row clearfix',
27606 initEvents : function()
27608 this.managerEl = this.el.select('.roo-document-manager', true).first();
27609 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27611 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
27612 this.selectorEl.hide();
27615 this.selectorEl.attr('multiple', 'multiple');
27618 this.selectorEl.on('change', this.onFileSelected, this);
27620 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
27621 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27623 this.uploader.on('click', this.onUploaderClick, this);
27625 this.renderProgressDialog();
27629 window.addEventListener("resize", function() { _this.refresh(); } );
27631 this.fireEvent('initial', this);
27634 renderProgressDialog : function()
27638 this.progressDialog = new Roo.bootstrap.Modal({
27639 cls : 'roo-document-manager-progress-dialog',
27640 allow_close : false,
27650 btnclick : function() {
27651 _this.uploadCancel();
27657 this.progressDialog.render(Roo.get(document.body));
27659 this.progress = new Roo.bootstrap.Progress({
27660 cls : 'roo-document-manager-progress',
27665 this.progress.render(this.progressDialog.getChildContainer());
27667 this.progressBar = new Roo.bootstrap.ProgressBar({
27668 cls : 'roo-document-manager-progress-bar',
27671 aria_valuemax : 12,
27675 this.progressBar.render(this.progress.getChildContainer());
27678 onUploaderClick : function(e)
27680 e.preventDefault();
27682 if(this.fireEvent('beforeselectfile', this) != false){
27683 this.selectorEl.dom.click();
27688 onFileSelected : function(e)
27690 e.preventDefault();
27692 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27696 Roo.each(this.selectorEl.dom.files, function(file){
27697 if(this.fireEvent('inspect', this, file) != false){
27698 this.files.push(file);
27708 this.selectorEl.dom.value = '';
27710 if(!this.files.length){
27714 if(this.boxes > 0 && this.files.length > this.boxes){
27715 this.files = this.files.slice(0, this.boxes);
27718 this.uploader.show();
27720 if(this.boxes > 0 && this.files.length > this.boxes - 1){
27721 this.uploader.hide();
27730 Roo.each(this.files, function(file){
27732 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27733 var f = this.renderPreview(file);
27738 if(file.type.indexOf('image') != -1){
27739 this.delegates.push(
27741 _this.process(file);
27742 }).createDelegate(this)
27750 _this.process(file);
27751 }).createDelegate(this)
27756 this.files = files;
27758 this.delegates = this.delegates.concat(docs);
27760 if(!this.delegates.length){
27765 this.progressBar.aria_valuemax = this.delegates.length;
27772 arrange : function()
27774 if(!this.delegates.length){
27775 this.progressDialog.hide();
27780 var delegate = this.delegates.shift();
27782 this.progressDialog.show();
27784 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
27786 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
27791 refresh : function()
27793 this.uploader.show();
27795 if(this.boxes > 0 && this.files.length > this.boxes - 1){
27796 this.uploader.hide();
27799 Roo.isTouch ? this.closable(false) : this.closable(true);
27801 this.fireEvent('refresh', this);
27804 onRemove : function(e, el, o)
27806 e.preventDefault();
27808 this.fireEvent('remove', this, o);
27812 remove : function(o)
27816 Roo.each(this.files, function(file){
27817 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
27826 this.files = files;
27833 Roo.each(this.files, function(file){
27838 file.target.remove();
27847 onClick : function(e, el, o)
27849 e.preventDefault();
27851 this.fireEvent('click', this, o);
27855 closable : function(closable)
27857 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
27859 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27871 xhrOnLoad : function(xhr)
27873 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27877 if (xhr.readyState !== 4) {
27879 this.fireEvent('exception', this, xhr);
27883 var response = Roo.decode(xhr.responseText);
27885 if(!response.success){
27887 this.fireEvent('exception', this, xhr);
27891 var file = this.renderPreview(response.data);
27893 this.files.push(file);
27897 this.fireEvent('afterupload', this, xhr);
27901 xhrOnError : function(xhr)
27903 Roo.log('xhr on error');
27905 var response = Roo.decode(xhr.responseText);
27912 process : function(file)
27914 if(this.fireEvent('process', this, file) !== false){
27915 if(this.editable && file.type.indexOf('image') != -1){
27916 this.fireEvent('edit', this, file);
27920 this.uploadStart(file, false);
27927 uploadStart : function(file, crop)
27929 this.xhr = new XMLHttpRequest();
27931 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27936 file.xhr = this.xhr;
27938 this.managerEl.createChild({
27940 cls : 'roo-document-manager-loading',
27944 tooltip : file.name,
27945 cls : 'roo-document-manager-thumb',
27946 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27952 this.xhr.open(this.method, this.url, true);
27955 "Accept": "application/json",
27956 "Cache-Control": "no-cache",
27957 "X-Requested-With": "XMLHttpRequest"
27960 for (var headerName in headers) {
27961 var headerValue = headers[headerName];
27963 this.xhr.setRequestHeader(headerName, headerValue);
27969 this.xhr.onload = function()
27971 _this.xhrOnLoad(_this.xhr);
27974 this.xhr.onerror = function()
27976 _this.xhrOnError(_this.xhr);
27979 var formData = new FormData();
27981 formData.append('returnHTML', 'NO');
27984 formData.append('crop', crop);
27987 formData.append(this.paramName, file, file.name);
27994 if(this.fireEvent('prepare', this, formData, options) != false){
27996 if(options.manually){
28000 this.xhr.send(formData);
28004 this.uploadCancel();
28007 uploadCancel : function()
28013 this.delegates = [];
28015 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28022 renderPreview : function(file)
28024 if(typeof(file.target) != 'undefined' && file.target){
28028 var previewEl = this.managerEl.createChild({
28030 cls : 'roo-document-manager-preview',
28034 tooltip : file[this.toolTipName],
28035 cls : 'roo-document-manager-thumb',
28036 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
28041 html : '<i class="fa fa-times-circle"></i>'
28046 var close = previewEl.select('button.close', true).first();
28048 close.on('click', this.onRemove, this, file);
28050 file.target = previewEl;
28052 var image = previewEl.select('img', true).first();
28056 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
28058 image.on('click', this.onClick, this, file);
28064 onPreviewLoad : function(file, image)
28066 if(typeof(file.target) == 'undefined' || !file.target){
28070 var width = image.dom.naturalWidth || image.dom.width;
28071 var height = image.dom.naturalHeight || image.dom.height;
28073 if(width > height){
28074 file.target.addClass('wide');
28078 file.target.addClass('tall');
28083 uploadFromSource : function(file, crop)
28085 this.xhr = new XMLHttpRequest();
28087 this.managerEl.createChild({
28089 cls : 'roo-document-manager-loading',
28093 tooltip : file.name,
28094 cls : 'roo-document-manager-thumb',
28095 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28101 this.xhr.open(this.method, this.url, true);
28104 "Accept": "application/json",
28105 "Cache-Control": "no-cache",
28106 "X-Requested-With": "XMLHttpRequest"
28109 for (var headerName in headers) {
28110 var headerValue = headers[headerName];
28112 this.xhr.setRequestHeader(headerName, headerValue);
28118 this.xhr.onload = function()
28120 _this.xhrOnLoad(_this.xhr);
28123 this.xhr.onerror = function()
28125 _this.xhrOnError(_this.xhr);
28128 var formData = new FormData();
28130 formData.append('returnHTML', 'NO');
28132 formData.append('crop', crop);
28134 if(typeof(file.filename) != 'undefined'){
28135 formData.append('filename', file.filename);
28138 if(typeof(file.mimetype) != 'undefined'){
28139 formData.append('mimetype', file.mimetype);
28142 if(this.fireEvent('prepare', this, formData) != false){
28143 this.xhr.send(formData);
28153 * @class Roo.bootstrap.DocumentViewer
28154 * @extends Roo.bootstrap.Component
28155 * Bootstrap DocumentViewer class
28156 * @cfg {Boolean} showDownload (true|false) show download button (default true)
28157 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
28160 * Create a new DocumentViewer
28161 * @param {Object} config The config object
28164 Roo.bootstrap.DocumentViewer = function(config){
28165 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
28170 * Fire after initEvent
28171 * @param {Roo.bootstrap.DocumentViewer} this
28177 * @param {Roo.bootstrap.DocumentViewer} this
28182 * Fire after download button
28183 * @param {Roo.bootstrap.DocumentViewer} this
28188 * Fire after trash button
28189 * @param {Roo.bootstrap.DocumentViewer} this
28196 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
28198 showDownload : true,
28202 getAutoCreate : function()
28206 cls : 'roo-document-viewer',
28210 cls : 'roo-document-viewer-body',
28214 cls : 'roo-document-viewer-thumb',
28218 cls : 'roo-document-viewer-image'
28226 cls : 'roo-document-viewer-footer',
28229 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
28233 cls : 'btn-group roo-document-viewer-download',
28237 cls : 'btn btn-default',
28238 html : '<i class="fa fa-download"></i>'
28244 cls : 'btn-group roo-document-viewer-trash',
28248 cls : 'btn btn-default',
28249 html : '<i class="fa fa-trash"></i>'
28262 initEvents : function()
28265 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
28266 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
28268 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
28269 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
28271 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
28272 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
28274 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
28275 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
28277 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
28278 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
28280 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
28281 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
28283 this.bodyEl.on('click', this.onClick, this);
28284 this.downloadBtn.on('click', this.onDownload, this);
28285 this.trashBtn.on('click', this.onTrash, this);
28287 this.downloadBtn.hide();
28288 this.trashBtn.hide();
28290 if(this.showDownload){
28291 this.downloadBtn.show();
28294 if(this.showTrash){
28295 this.trashBtn.show();
28298 if(!this.showDownload && !this.showTrash) {
28299 this.footerEl.hide();
28304 initial : function()
28306 // this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
28309 this.fireEvent('initial', this);
28313 onClick : function(e)
28315 e.preventDefault();
28317 this.fireEvent('click', this);
28320 onDownload : function(e)
28322 e.preventDefault();
28324 this.fireEvent('download', this);
28327 onTrash : function(e)
28329 e.preventDefault();
28331 this.fireEvent('trash', this);
28343 * @class Roo.bootstrap.NavProgressBar
28344 * @extends Roo.bootstrap.Component
28345 * Bootstrap NavProgressBar class
28348 * Create a new nav progress bar
28349 * @param {Object} config The config object
28352 Roo.bootstrap.NavProgressBar = function(config){
28353 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
28355 this.bullets = this.bullets || [];
28357 // Roo.bootstrap.NavProgressBar.register(this);
28361 * Fires when the active item changes
28362 * @param {Roo.bootstrap.NavProgressBar} this
28363 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
28364 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
28371 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
28376 getAutoCreate : function()
28378 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
28382 cls : 'roo-navigation-bar-group',
28386 cls : 'roo-navigation-top-bar'
28390 cls : 'roo-navigation-bullets-bar',
28394 cls : 'roo-navigation-bar'
28401 cls : 'roo-navigation-bottom-bar'
28411 initEvents: function()
28416 onRender : function(ct, position)
28418 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28420 if(this.bullets.length){
28421 Roo.each(this.bullets, function(b){
28430 addItem : function(cfg)
28432 var item = new Roo.bootstrap.NavProgressItem(cfg);
28434 item.parentId = this.id;
28435 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
28438 var top = new Roo.bootstrap.Element({
28440 cls : 'roo-navigation-bar-text'
28443 var bottom = new Roo.bootstrap.Element({
28445 cls : 'roo-navigation-bar-text'
28448 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
28449 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
28451 var topText = new Roo.bootstrap.Element({
28453 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
28456 var bottomText = new Roo.bootstrap.Element({
28458 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
28461 topText.onRender(top.el, null);
28462 bottomText.onRender(bottom.el, null);
28465 item.bottomEl = bottom;
28468 this.barItems.push(item);
28473 getActive : function()
28475 var active = false;
28477 Roo.each(this.barItems, function(v){
28479 if (!v.isActive()) {
28491 setActiveItem : function(item)
28495 Roo.each(this.barItems, function(v){
28496 if (v.rid == item.rid) {
28500 if (v.isActive()) {
28501 v.setActive(false);
28506 item.setActive(true);
28508 this.fireEvent('changed', this, item, prev);
28511 getBarItem: function(rid)
28515 Roo.each(this.barItems, function(e) {
28516 if (e.rid != rid) {
28527 indexOfItem : function(item)
28531 Roo.each(this.barItems, function(v, i){
28533 if (v.rid != item.rid) {
28544 setActiveNext : function()
28546 var i = this.indexOfItem(this.getActive());
28548 if (i > this.barItems.length) {
28552 this.setActiveItem(this.barItems[i+1]);
28555 setActivePrev : function()
28557 var i = this.indexOfItem(this.getActive());
28563 this.setActiveItem(this.barItems[i-1]);
28566 format : function()
28568 if(!this.barItems.length){
28572 var width = 100 / this.barItems.length;
28574 Roo.each(this.barItems, function(i){
28575 i.el.setStyle('width', width + '%');
28576 i.topEl.el.setStyle('width', width + '%');
28577 i.bottomEl.el.setStyle('width', width + '%');
28586 * Nav Progress Item
28591 * @class Roo.bootstrap.NavProgressItem
28592 * @extends Roo.bootstrap.Component
28593 * Bootstrap NavProgressItem class
28594 * @cfg {String} rid the reference id
28595 * @cfg {Boolean} active (true|false) Is item active default false
28596 * @cfg {Boolean} disabled (true|false) Is item active default false
28597 * @cfg {String} html
28598 * @cfg {String} position (top|bottom) text position default bottom
28599 * @cfg {String} icon show icon instead of number
28602 * Create a new NavProgressItem
28603 * @param {Object} config The config object
28605 Roo.bootstrap.NavProgressItem = function(config){
28606 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
28611 * The raw click event for the entire grid.
28612 * @param {Roo.bootstrap.NavProgressItem} this
28613 * @param {Roo.EventObject} e
28620 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
28626 position : 'bottom',
28629 getAutoCreate : function()
28631 var iconCls = 'roo-navigation-bar-item-icon';
28633 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
28637 cls: 'roo-navigation-bar-item',
28647 cfg.cls += ' active';
28650 cfg.cls += ' disabled';
28656 disable : function()
28658 this.setDisabled(true);
28661 enable : function()
28663 this.setDisabled(false);
28666 initEvents: function()
28668 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
28670 this.iconEl.on('click', this.onClick, this);
28673 onClick : function(e)
28675 e.preventDefault();
28681 if(this.fireEvent('click', this, e) === false){
28685 this.parent().setActiveItem(this);
28688 isActive: function ()
28690 return this.active;
28693 setActive : function(state)
28695 if(this.active == state){
28699 this.active = state;
28702 this.el.addClass('active');
28706 this.el.removeClass('active');
28711 setDisabled : function(state)
28713 if(this.disabled == state){
28717 this.disabled = state;
28720 this.el.addClass('disabled');
28724 this.el.removeClass('disabled');
28727 tooltipEl : function()
28729 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
28742 * @class Roo.bootstrap.FieldLabel
28743 * @extends Roo.bootstrap.Component
28744 * Bootstrap FieldLabel class
28745 * @cfg {String} html contents of the element
28746 * @cfg {String} tag tag of the element default label
28747 * @cfg {String} cls class of the element
28748 * @cfg {String} target label target
28749 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
28750 * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
28751 * @cfg {String} validClass default "text-success fa fa-lg fa-check"
28752 * @cfg {String} iconTooltip default "This field is required"
28755 * Create a new FieldLabel
28756 * @param {Object} config The config object
28759 Roo.bootstrap.FieldLabel = function(config){
28760 Roo.bootstrap.Element.superclass.constructor.call(this, config);
28765 * Fires after the field has been marked as invalid.
28766 * @param {Roo.form.FieldLabel} this
28767 * @param {String} msg The validation message
28772 * Fires after the field has been validated with no errors.
28773 * @param {Roo.form.FieldLabel} this
28779 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
28786 invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
28787 validClass : 'text-success fa fa-lg fa-check',
28788 iconTooltip : 'This field is required',
28790 getAutoCreate : function(){
28794 cls : 'roo-bootstrap-field-label ' + this.cls,
28800 tooltip : this.iconTooltip
28812 initEvents: function()
28814 Roo.bootstrap.Element.superclass.initEvents.call(this);
28816 this.iconEl = this.el.select('i', true).first();
28818 this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
28820 Roo.bootstrap.FieldLabel.register(this);
28824 * Mark this field as valid
28826 markValid : function()
28828 this.iconEl.show();
28830 this.iconEl.removeClass(this.invalidClass);
28832 this.iconEl.addClass(this.validClass);
28834 this.fireEvent('valid', this);
28838 * Mark this field as invalid
28839 * @param {String} msg The validation message
28841 markInvalid : function(msg)
28843 this.iconEl.show();
28845 this.iconEl.removeClass(this.validClass);
28847 this.iconEl.addClass(this.invalidClass);
28849 this.fireEvent('invalid', this, msg);
28855 Roo.apply(Roo.bootstrap.FieldLabel, {
28860 * register a FieldLabel Group
28861 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
28863 register : function(label)
28865 if(this.groups.hasOwnProperty(label.target)){
28869 this.groups[label.target] = label;
28873 * fetch a FieldLabel Group based on the target
28874 * @param {string} target
28875 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
28877 get: function(target) {
28878 if (typeof(this.groups[target]) == 'undefined') {
28882 return this.groups[target] ;
28891 * page DateSplitField.
28897 * @class Roo.bootstrap.DateSplitField
28898 * @extends Roo.bootstrap.Component
28899 * Bootstrap DateSplitField class
28900 * @cfg {string} fieldLabel - the label associated
28901 * @cfg {Number} labelWidth set the width of label (0-12)
28902 * @cfg {String} labelAlign (top|left)
28903 * @cfg {Boolean} dayAllowBlank (true|false) default false
28904 * @cfg {Boolean} monthAllowBlank (true|false) default false
28905 * @cfg {Boolean} yearAllowBlank (true|false) default false
28906 * @cfg {string} dayPlaceholder
28907 * @cfg {string} monthPlaceholder
28908 * @cfg {string} yearPlaceholder
28909 * @cfg {string} dayFormat default 'd'
28910 * @cfg {string} monthFormat default 'm'
28911 * @cfg {string} yearFormat default 'Y'
28915 * Create a new DateSplitField
28916 * @param {Object} config The config object
28919 Roo.bootstrap.DateSplitField = function(config){
28920 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
28926 * getting the data of years
28927 * @param {Roo.bootstrap.DateSplitField} this
28928 * @param {Object} years
28933 * getting the data of days
28934 * @param {Roo.bootstrap.DateSplitField} this
28935 * @param {Object} days
28940 * Fires after the field has been marked as invalid.
28941 * @param {Roo.form.Field} this
28942 * @param {String} msg The validation message
28947 * Fires after the field has been validated with no errors.
28948 * @param {Roo.form.Field} this
28954 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
28957 labelAlign : 'top',
28959 dayAllowBlank : false,
28960 monthAllowBlank : false,
28961 yearAllowBlank : false,
28962 dayPlaceholder : '',
28963 monthPlaceholder : '',
28964 yearPlaceholder : '',
28968 isFormField : true,
28970 getAutoCreate : function()
28974 cls : 'row roo-date-split-field-group',
28979 cls : 'form-hidden-field roo-date-split-field-group-value',
28985 if(this.fieldLabel){
28988 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
28992 html : this.fieldLabel
28998 Roo.each(['day', 'month', 'year'], function(t){
29001 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
29008 inputEl: function ()
29010 return this.el.select('.roo-date-split-field-group-value', true).first();
29013 onRender : function(ct, position)
29017 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29019 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
29021 this.dayField = new Roo.bootstrap.ComboBox({
29022 allowBlank : this.dayAllowBlank,
29023 alwaysQuery : true,
29024 displayField : 'value',
29027 forceSelection : true,
29029 placeholder : this.dayPlaceholder,
29030 selectOnFocus : true,
29031 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29032 triggerAction : 'all',
29034 valueField : 'value',
29035 store : new Roo.data.SimpleStore({
29036 data : (function() {
29038 _this.fireEvent('days', _this, days);
29041 fields : [ 'value' ]
29044 select : function (_self, record, index)
29046 _this.setValue(_this.getValue());
29051 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
29053 this.monthField = new Roo.bootstrap.MonthField({
29054 after : '<i class=\"fa fa-calendar\"></i>',
29055 allowBlank : this.monthAllowBlank,
29056 placeholder : this.monthPlaceholder,
29059 render : function (_self)
29061 this.el.select('span.input-group-addon', true).first().on('click', function(e){
29062 e.preventDefault();
29066 select : function (_self, oldvalue, newvalue)
29068 _this.setValue(_this.getValue());
29073 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
29075 this.yearField = new Roo.bootstrap.ComboBox({
29076 allowBlank : this.yearAllowBlank,
29077 alwaysQuery : true,
29078 displayField : 'value',
29081 forceSelection : true,
29083 placeholder : this.yearPlaceholder,
29084 selectOnFocus : true,
29085 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29086 triggerAction : 'all',
29088 valueField : 'value',
29089 store : new Roo.data.SimpleStore({
29090 data : (function() {
29092 _this.fireEvent('years', _this, years);
29095 fields : [ 'value' ]
29098 select : function (_self, record, index)
29100 _this.setValue(_this.getValue());
29105 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
29108 setValue : function(v, format)
29110 this.inputEl.dom.value = v;
29112 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
29114 var d = Date.parseDate(v, f);
29121 this.setDay(d.format(this.dayFormat));
29122 this.setMonth(d.format(this.monthFormat));
29123 this.setYear(d.format(this.yearFormat));
29130 setDay : function(v)
29132 this.dayField.setValue(v);
29133 this.inputEl.dom.value = this.getValue();
29138 setMonth : function(v)
29140 this.monthField.setValue(v, true);
29141 this.inputEl.dom.value = this.getValue();
29146 setYear : function(v)
29148 this.yearField.setValue(v);
29149 this.inputEl.dom.value = this.getValue();
29154 getDay : function()
29156 return this.dayField.getValue();
29159 getMonth : function()
29161 return this.monthField.getValue();
29164 getYear : function()
29166 return this.yearField.getValue();
29169 getValue : function()
29171 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
29173 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
29183 this.inputEl.dom.value = '';
29188 validate : function()
29190 var d = this.dayField.validate();
29191 var m = this.monthField.validate();
29192 var y = this.yearField.validate();
29197 (!this.dayAllowBlank && !d) ||
29198 (!this.monthAllowBlank && !m) ||
29199 (!this.yearAllowBlank && !y)
29204 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
29213 this.markInvalid();
29218 markValid : function()
29221 var label = this.el.select('label', true).first();
29222 var icon = this.el.select('i.fa-star', true).first();
29228 this.fireEvent('valid', this);
29232 * Mark this field as invalid
29233 * @param {String} msg The validation message
29235 markInvalid : function(msg)
29238 var label = this.el.select('label', true).first();
29239 var icon = this.el.select('i.fa-star', true).first();
29241 if(label && !icon){
29242 this.el.select('.roo-date-split-field-label', true).createChild({
29244 cls : 'text-danger fa fa-lg fa-star',
29245 tooltip : 'This field is required',
29246 style : 'margin-right:5px;'
29250 this.fireEvent('invalid', this, msg);
29253 clearInvalid : function()
29255 var label = this.el.select('label', true).first();
29256 var icon = this.el.select('i.fa-star', true).first();
29262 this.fireEvent('valid', this);
29265 getName: function()
29275 * http://masonry.desandro.com
29277 * The idea is to render all the bricks based on vertical width...
29279 * The original code extends 'outlayer' - we might need to use that....
29285 * @class Roo.bootstrap.LayoutMasonry
29286 * @extends Roo.bootstrap.Component
29287 * Bootstrap Layout Masonry class
29290 * Create a new Element
29291 * @param {Object} config The config object
29294 Roo.bootstrap.LayoutMasonry = function(config){
29295 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
29301 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
29304 * @cfg {Boolean} isLayoutInstant = no animation?
29306 isLayoutInstant : false, // needed?
29309 * @cfg {Number} boxWidth width of the columns
29314 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
29319 * @cfg {Number} padWidth padding below box..
29324 * @cfg {Number} gutter gutter width..
29329 * @cfg {Number} maxCols maximum number of columns
29335 * @cfg {Boolean} isAutoInitial defalut true
29337 isAutoInitial : true,
29342 * @cfg {Boolean} isHorizontal defalut false
29344 isHorizontal : false,
29346 currentSize : null,
29352 bricks: null, //CompositeElement
29356 _isLayoutInited : false,
29358 // isAlternative : false, // only use for vertical layout...
29361 * @cfg {Number} alternativePadWidth padding below box..
29363 alternativePadWidth : 50,
29365 getAutoCreate : function(){
29369 cls: 'blog-masonary-wrapper ' + this.cls,
29371 cls : 'mas-boxes masonary'
29378 getChildContainer: function( )
29380 if (this.boxesEl) {
29381 return this.boxesEl;
29384 this.boxesEl = this.el.select('.mas-boxes').first();
29386 return this.boxesEl;
29390 initEvents : function()
29394 if(this.isAutoInitial){
29395 Roo.log('hook children rendered');
29396 this.on('childrenrendered', function() {
29397 Roo.log('children rendered');
29403 initial : function()
29405 this.currentSize = this.el.getBox(true);
29407 Roo.EventManager.onWindowResize(this.resize, this);
29409 if(!this.isAutoInitial){
29417 //this.layout.defer(500,this);
29421 resize : function()
29425 var cs = this.el.getBox(true);
29427 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
29428 Roo.log("no change in with or X");
29432 this.currentSize = cs;
29438 layout : function()
29440 this._resetLayout();
29442 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
29444 this.layoutItems( isInstant );
29446 this._isLayoutInited = true;
29450 _resetLayout : function()
29452 if(this.isHorizontal){
29453 this.horizontalMeasureColumns();
29457 this.verticalMeasureColumns();
29461 verticalMeasureColumns : function()
29463 this.getContainerWidth();
29465 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29466 // this.colWidth = Math.floor(this.containerWidth * 0.8);
29470 var boxWidth = this.boxWidth + this.padWidth;
29472 if(this.containerWidth < this.boxWidth){
29473 boxWidth = this.containerWidth
29476 var containerWidth = this.containerWidth;
29478 var cols = Math.floor(containerWidth / boxWidth);
29480 this.cols = Math.max( cols, 1 );
29482 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
29484 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
29486 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
29488 this.colWidth = boxWidth + avail - this.padWidth;
29490 this.unitWidth = Math.floor((this.colWidth - (this.gutter * 2)) / 3);
29491 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
29494 horizontalMeasureColumns : function()
29496 this.getContainerWidth();
29498 var boxWidth = this.boxWidth;
29500 if(this.containerWidth < boxWidth){
29501 boxWidth = this.containerWidth;
29504 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
29506 this.el.setHeight(boxWidth);
29510 getContainerWidth : function()
29512 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
29515 layoutItems : function( isInstant )
29517 var items = Roo.apply([], this.bricks);
29519 if(this.isHorizontal){
29520 this._horizontalLayoutItems( items , isInstant );
29524 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29525 // this._verticalAlternativeLayoutItems( items , isInstant );
29529 this._verticalLayoutItems( items , isInstant );
29533 _verticalLayoutItems : function ( items , isInstant)
29535 if ( !items || !items.length ) {
29540 ['xs', 'xs', 'xs', 'tall'],
29541 ['xs', 'xs', 'tall'],
29542 ['xs', 'xs', 'sm'],
29543 ['xs', 'xs', 'xs'],
29549 ['sm', 'xs', 'xs'],
29553 ['tall', 'xs', 'xs', 'xs'],
29554 ['tall', 'xs', 'xs'],
29566 Roo.each(items, function(item, k){
29568 switch (item.size) {
29569 // these layouts take up a full box,
29580 boxes.push([item]);
29603 var filterPattern = function(box, length)
29611 var pattern = box.slice(0, length);
29615 Roo.each(pattern, function(i){
29616 format.push(i.size);
29619 Roo.each(standard, function(s){
29621 if(String(s) != String(format)){
29630 if(!match && length == 1){
29635 filterPattern(box, length - 1);
29639 queue.push(pattern);
29641 box = box.slice(length, box.length);
29643 filterPattern(box, 4);
29649 Roo.each(boxes, function(box, k){
29655 if(box.length == 1){
29660 filterPattern(box, 4);
29664 this._processVerticalLayoutQueue( queue, isInstant );
29668 // _verticalAlternativeLayoutItems : function( items , isInstant )
29670 // if ( !items || !items.length ) {
29674 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
29678 _horizontalLayoutItems : function ( items , isInstant)
29680 if ( !items || !items.length || items.length < 3) {
29686 var eItems = items.slice(0, 3);
29688 items = items.slice(3, items.length);
29691 ['xs', 'xs', 'xs', 'wide'],
29692 ['xs', 'xs', 'wide'],
29693 ['xs', 'xs', 'sm'],
29694 ['xs', 'xs', 'xs'],
29700 ['sm', 'xs', 'xs'],
29704 ['wide', 'xs', 'xs', 'xs'],
29705 ['wide', 'xs', 'xs'],
29718 Roo.each(items, function(item, k){
29720 switch (item.size) {
29731 boxes.push([item]);
29755 var filterPattern = function(box, length)
29763 var pattern = box.slice(0, length);
29767 Roo.each(pattern, function(i){
29768 format.push(i.size);
29771 Roo.each(standard, function(s){
29773 if(String(s) != String(format)){
29782 if(!match && length == 1){
29787 filterPattern(box, length - 1);
29791 queue.push(pattern);
29793 box = box.slice(length, box.length);
29795 filterPattern(box, 4);
29801 Roo.each(boxes, function(box, k){
29807 if(box.length == 1){
29812 filterPattern(box, 4);
29819 var pos = this.el.getBox(true);
29823 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29825 var hit_end = false;
29827 Roo.each(queue, function(box){
29831 Roo.each(box, function(b){
29833 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29843 Roo.each(box, function(b){
29845 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29848 mx = Math.max(mx, b.x);
29852 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
29856 Roo.each(box, function(b){
29858 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29872 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
29875 /** Sets position of item in DOM
29876 * @param {Element} item
29877 * @param {Number} x - horizontal position
29878 * @param {Number} y - vertical position
29879 * @param {Boolean} isInstant - disables transitions
29881 _processVerticalLayoutQueue : function( queue, isInstant )
29883 var pos = this.el.getBox(true);
29888 for (var i = 0; i < this.cols; i++){
29892 Roo.each(queue, function(box, k){
29894 var col = k % this.cols;
29896 Roo.each(box, function(b,kk){
29898 b.el.position('absolute');
29900 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29901 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29903 if(b.size == 'md-left' || b.size == 'md-right'){
29904 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29905 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29908 b.el.setWidth(width);
29909 b.el.setHeight(height);
29911 b.el.select('iframe',true).setSize(width,height);
29915 for (var i = 0; i < this.cols; i++){
29917 if(maxY[i] < maxY[col]){
29922 col = Math.min(col, i);
29926 x = pos.x + col * (this.colWidth + this.padWidth);
29930 var positions = [];
29932 switch (box.length){
29934 positions = this.getVerticalOneBoxColPositions(x, y, box);
29937 positions = this.getVerticalTwoBoxColPositions(x, y, box);
29940 positions = this.getVerticalThreeBoxColPositions(x, y, box);
29943 positions = this.getVerticalFourBoxColPositions(x, y, box);
29949 Roo.each(box, function(b,kk){
29951 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29953 var sz = b.el.getSize();
29955 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
29963 for (var i = 0; i < this.cols; i++){
29964 mY = Math.max(mY, maxY[i]);
29967 this.el.setHeight(mY - pos.y);
29971 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
29973 // var pos = this.el.getBox(true);
29976 // var maxX = pos.right;
29978 // var maxHeight = 0;
29980 // Roo.each(items, function(item, k){
29984 // item.el.position('absolute');
29986 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
29988 // item.el.setWidth(width);
29990 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
29992 // item.el.setHeight(height);
29995 // item.el.setXY([x, y], isInstant ? false : true);
29997 // item.el.setXY([maxX - width, y], isInstant ? false : true);
30000 // y = y + height + this.alternativePadWidth;
30002 // maxHeight = maxHeight + height + this.alternativePadWidth;
30006 // this.el.setHeight(maxHeight);
30010 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
30012 var pos = this.el.getBox(true);
30017 var maxX = pos.right;
30019 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
30021 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30023 Roo.each(queue, function(box, k){
30025 Roo.each(box, function(b, kk){
30027 b.el.position('absolute');
30029 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30030 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30032 if(b.size == 'md-left' || b.size == 'md-right'){
30033 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30034 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30037 b.el.setWidth(width);
30038 b.el.setHeight(height);
30046 var positions = [];
30048 switch (box.length){
30050 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
30053 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
30056 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
30059 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
30065 Roo.each(box, function(b,kk){
30067 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30069 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
30077 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
30079 Roo.each(eItems, function(b,k){
30081 b.size = (k == 0) ? 'sm' : 'xs';
30082 b.x = (k == 0) ? 2 : 1;
30083 b.y = (k == 0) ? 2 : 1;
30085 b.el.position('absolute');
30087 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30089 b.el.setWidth(width);
30091 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30093 b.el.setHeight(height);
30097 var positions = [];
30100 x : maxX - this.unitWidth * 2 - this.gutter,
30105 x : maxX - this.unitWidth,
30106 y : minY + (this.unitWidth + this.gutter) * 2
30110 x : maxX - this.unitWidth * 3 - this.gutter * 2,
30114 Roo.each(eItems, function(b,k){
30116 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
30122 getVerticalOneBoxColPositions : function(x, y, box)
30126 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
30128 if(box[0].size == 'md-left'){
30132 if(box[0].size == 'md-right'){
30137 x : x + (this.unitWidth + this.gutter) * rand,
30144 getVerticalTwoBoxColPositions : function(x, y, box)
30148 if(box[0].size == 'xs'){
30152 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
30156 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
30170 x : x + (this.unitWidth + this.gutter) * 2,
30171 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
30178 getVerticalThreeBoxColPositions : function(x, y, box)
30182 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
30190 x : x + (this.unitWidth + this.gutter) * 1,
30195 x : x + (this.unitWidth + this.gutter) * 2,
30203 if(box[0].size == 'xs' && box[1].size == 'xs'){
30212 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
30216 x : x + (this.unitWidth + this.gutter) * 1,
30230 x : x + (this.unitWidth + this.gutter) * 2,
30235 x : x + (this.unitWidth + this.gutter) * 2,
30236 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
30243 getVerticalFourBoxColPositions : function(x, y, box)
30247 if(box[0].size == 'xs'){
30256 y : y + (this.unitHeight + this.gutter) * 1
30261 y : y + (this.unitHeight + this.gutter) * 2
30265 x : x + (this.unitWidth + this.gutter) * 1,
30279 x : x + (this.unitWidth + this.gutter) * 2,
30284 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
30285 y : y + (this.unitHeight + this.gutter) * 1
30289 x : x + (this.unitWidth + this.gutter) * 2,
30290 y : y + (this.unitWidth + this.gutter) * 2
30297 getHorizontalOneBoxColPositions : function(maxX, minY, box)
30301 if(box[0].size == 'md-left'){
30303 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
30310 if(box[0].size == 'md-right'){
30312 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
30313 y : minY + (this.unitWidth + this.gutter) * 1
30319 var rand = Math.floor(Math.random() * (4 - box[0].y));
30322 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30323 y : minY + (this.unitWidth + this.gutter) * rand
30330 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
30334 if(box[0].size == 'xs'){
30337 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30342 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30343 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
30351 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30356 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30357 y : minY + (this.unitWidth + this.gutter) * 2
30364 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
30368 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
30371 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30376 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30377 y : minY + (this.unitWidth + this.gutter) * 1
30381 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30382 y : minY + (this.unitWidth + this.gutter) * 2
30389 if(box[0].size == 'xs' && box[1].size == 'xs'){
30392 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30397 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30402 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30403 y : minY + (this.unitWidth + this.gutter) * 1
30411 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30416 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30417 y : minY + (this.unitWidth + this.gutter) * 2
30421 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30422 y : minY + (this.unitWidth + this.gutter) * 2
30429 getHorizontalFourBoxColPositions : function(maxX, minY, box)
30433 if(box[0].size == 'xs'){
30436 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30441 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30446 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),
30451 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
30452 y : minY + (this.unitWidth + this.gutter) * 1
30460 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30465 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30466 y : minY + (this.unitWidth + this.gutter) * 2
30470 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30471 y : minY + (this.unitWidth + this.gutter) * 2
30475 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),
30476 y : minY + (this.unitWidth + this.gutter) * 2
30490 * http://masonry.desandro.com
30492 * The idea is to render all the bricks based on vertical width...
30494 * The original code extends 'outlayer' - we might need to use that....
30500 * @class Roo.bootstrap.LayoutMasonryAuto
30501 * @extends Roo.bootstrap.Component
30502 * Bootstrap Layout Masonry class
30505 * Create a new Element
30506 * @param {Object} config The config object
30509 Roo.bootstrap.LayoutMasonryAuto = function(config){
30510 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
30513 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
30516 * @cfg {Boolean} isFitWidth - resize the width..
30518 isFitWidth : false, // options..
30520 * @cfg {Boolean} isOriginLeft = left align?
30522 isOriginLeft : true,
30524 * @cfg {Boolean} isOriginTop = top align?
30526 isOriginTop : false,
30528 * @cfg {Boolean} isLayoutInstant = no animation?
30530 isLayoutInstant : false, // needed?
30532 * @cfg {Boolean} isResizingContainer = not sure if this is used..
30534 isResizingContainer : true,
30536 * @cfg {Number} columnWidth width of the columns
30542 * @cfg {Number} maxCols maximum number of columns
30547 * @cfg {Number} padHeight padding below box..
30553 * @cfg {Boolean} isAutoInitial defalut true
30556 isAutoInitial : true,
30562 initialColumnWidth : 0,
30563 currentSize : null,
30565 colYs : null, // array.
30572 bricks: null, //CompositeElement
30573 cols : 0, // array?
30574 // element : null, // wrapped now this.el
30575 _isLayoutInited : null,
30578 getAutoCreate : function(){
30582 cls: 'blog-masonary-wrapper ' + this.cls,
30584 cls : 'mas-boxes masonary'
30591 getChildContainer: function( )
30593 if (this.boxesEl) {
30594 return this.boxesEl;
30597 this.boxesEl = this.el.select('.mas-boxes').first();
30599 return this.boxesEl;
30603 initEvents : function()
30607 if(this.isAutoInitial){
30608 Roo.log('hook children rendered');
30609 this.on('childrenrendered', function() {
30610 Roo.log('children rendered');
30617 initial : function()
30619 this.reloadItems();
30621 this.currentSize = this.el.getBox(true);
30623 /// was window resize... - let's see if this works..
30624 Roo.EventManager.onWindowResize(this.resize, this);
30626 if(!this.isAutoInitial){
30631 this.layout.defer(500,this);
30634 reloadItems: function()
30636 this.bricks = this.el.select('.masonry-brick', true);
30638 this.bricks.each(function(b) {
30639 //Roo.log(b.getSize());
30640 if (!b.attr('originalwidth')) {
30641 b.attr('originalwidth', b.getSize().width);
30646 Roo.log(this.bricks.elements.length);
30649 resize : function()
30652 var cs = this.el.getBox(true);
30654 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
30655 Roo.log("no change in with or X");
30658 this.currentSize = cs;
30662 layout : function()
30665 this._resetLayout();
30666 //this._manageStamps();
30668 // don't animate first layout
30669 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30670 this.layoutItems( isInstant );
30672 // flag for initalized
30673 this._isLayoutInited = true;
30676 layoutItems : function( isInstant )
30678 //var items = this._getItemsForLayout( this.items );
30679 // original code supports filtering layout items.. we just ignore it..
30681 this._layoutItems( this.bricks , isInstant );
30683 this._postLayout();
30685 _layoutItems : function ( items , isInstant)
30687 //this.fireEvent( 'layout', this, items );
30690 if ( !items || !items.elements.length ) {
30691 // no items, emit event with empty array
30696 items.each(function(item) {
30697 Roo.log("layout item");
30699 // get x/y object from method
30700 var position = this._getItemLayoutPosition( item );
30702 position.item = item;
30703 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
30704 queue.push( position );
30707 this._processLayoutQueue( queue );
30709 /** Sets position of item in DOM
30710 * @param {Element} item
30711 * @param {Number} x - horizontal position
30712 * @param {Number} y - vertical position
30713 * @param {Boolean} isInstant - disables transitions
30715 _processLayoutQueue : function( queue )
30717 for ( var i=0, len = queue.length; i < len; i++ ) {
30718 var obj = queue[i];
30719 obj.item.position('absolute');
30720 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
30726 * Any logic you want to do after each layout,
30727 * i.e. size the container
30729 _postLayout : function()
30731 this.resizeContainer();
30734 resizeContainer : function()
30736 if ( !this.isResizingContainer ) {
30739 var size = this._getContainerSize();
30741 this.el.setSize(size.width,size.height);
30742 this.boxesEl.setSize(size.width,size.height);
30748 _resetLayout : function()
30750 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
30751 this.colWidth = this.el.getWidth();
30752 //this.gutter = this.el.getWidth();
30754 this.measureColumns();
30760 this.colYs.push( 0 );
30766 measureColumns : function()
30768 this.getContainerWidth();
30769 // if columnWidth is 0, default to outerWidth of first item
30770 if ( !this.columnWidth ) {
30771 var firstItem = this.bricks.first();
30772 Roo.log(firstItem);
30773 this.columnWidth = this.containerWidth;
30774 if (firstItem && firstItem.attr('originalwidth') ) {
30775 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
30777 // columnWidth fall back to item of first element
30778 Roo.log("set column width?");
30779 this.initialColumnWidth = this.columnWidth ;
30781 // if first elem has no width, default to size of container
30786 if (this.initialColumnWidth) {
30787 this.columnWidth = this.initialColumnWidth;
30792 // column width is fixed at the top - however if container width get's smaller we should
30795 // this bit calcs how man columns..
30797 var columnWidth = this.columnWidth += this.gutter;
30799 // calculate columns
30800 var containerWidth = this.containerWidth + this.gutter;
30802 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
30803 // fix rounding errors, typically with gutters
30804 var excess = columnWidth - containerWidth % columnWidth;
30807 // if overshoot is less than a pixel, round up, otherwise floor it
30808 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
30809 cols = Math[ mathMethod ]( cols );
30810 this.cols = Math.max( cols, 1 );
30811 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30813 // padding positioning..
30814 var totalColWidth = this.cols * this.columnWidth;
30815 var padavail = this.containerWidth - totalColWidth;
30816 // so for 2 columns - we need 3 'pads'
30818 var padNeeded = (1+this.cols) * this.padWidth;
30820 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
30822 this.columnWidth += padExtra
30823 //this.padWidth = Math.floor(padavail / ( this.cols));
30825 // adjust colum width so that padding is fixed??
30827 // we have 3 columns ... total = width * 3
30828 // we have X left over... that should be used by
30830 //if (this.expandC) {
30838 getContainerWidth : function()
30840 /* // container is parent if fit width
30841 var container = this.isFitWidth ? this.element.parentNode : this.element;
30842 // check that this.size and size are there
30843 // IE8 triggers resize on body size change, so they might not be
30845 var size = getSize( container ); //FIXME
30846 this.containerWidth = size && size.innerWidth; //FIXME
30849 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
30853 _getItemLayoutPosition : function( item ) // what is item?
30855 // we resize the item to our columnWidth..
30857 item.setWidth(this.columnWidth);
30858 item.autoBoxAdjust = false;
30860 var sz = item.getSize();
30862 // how many columns does this brick span
30863 var remainder = this.containerWidth % this.columnWidth;
30865 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
30866 // round if off by 1 pixel, otherwise use ceil
30867 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
30868 colSpan = Math.min( colSpan, this.cols );
30870 // normally this should be '1' as we dont' currently allow multi width columns..
30872 var colGroup = this._getColGroup( colSpan );
30873 // get the minimum Y value from the columns
30874 var minimumY = Math.min.apply( Math, colGroup );
30875 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
30877 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
30879 // position the brick
30881 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
30882 y: this.currentSize.y + minimumY + this.padHeight
30886 // apply setHeight to necessary columns
30887 var setHeight = minimumY + sz.height + this.padHeight;
30888 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
30890 var setSpan = this.cols + 1 - colGroup.length;
30891 for ( var i = 0; i < setSpan; i++ ) {
30892 this.colYs[ shortColIndex + i ] = setHeight ;
30899 * @param {Number} colSpan - number of columns the element spans
30900 * @returns {Array} colGroup
30902 _getColGroup : function( colSpan )
30904 if ( colSpan < 2 ) {
30905 // if brick spans only one column, use all the column Ys
30910 // how many different places could this brick fit horizontally
30911 var groupCount = this.cols + 1 - colSpan;
30912 // for each group potential horizontal position
30913 for ( var i = 0; i < groupCount; i++ ) {
30914 // make an array of colY values for that one group
30915 var groupColYs = this.colYs.slice( i, i + colSpan );
30916 // and get the max value of the array
30917 colGroup[i] = Math.max.apply( Math, groupColYs );
30922 _manageStamp : function( stamp )
30924 var stampSize = stamp.getSize();
30925 var offset = stamp.getBox();
30926 // get the columns that this stamp affects
30927 var firstX = this.isOriginLeft ? offset.x : offset.right;
30928 var lastX = firstX + stampSize.width;
30929 var firstCol = Math.floor( firstX / this.columnWidth );
30930 firstCol = Math.max( 0, firstCol );
30932 var lastCol = Math.floor( lastX / this.columnWidth );
30933 // lastCol should not go over if multiple of columnWidth #425
30934 lastCol -= lastX % this.columnWidth ? 0 : 1;
30935 lastCol = Math.min( this.cols - 1, lastCol );
30937 // set colYs to bottom of the stamp
30938 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
30941 for ( var i = firstCol; i <= lastCol; i++ ) {
30942 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
30947 _getContainerSize : function()
30949 this.maxY = Math.max.apply( Math, this.colYs );
30954 if ( this.isFitWidth ) {
30955 size.width = this._getContainerFitWidth();
30961 _getContainerFitWidth : function()
30963 var unusedCols = 0;
30964 // count unused columns
30967 if ( this.colYs[i] !== 0 ) {
30972 // fit container to columns that have been used
30973 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
30976 needsResizeLayout : function()
30978 var previousWidth = this.containerWidth;
30979 this.getContainerWidth();
30980 return previousWidth !== this.containerWidth;
30995 * @class Roo.bootstrap.MasonryBrick
30996 * @extends Roo.bootstrap.Component
30997 * Bootstrap MasonryBrick class
31000 * Create a new MasonryBrick
31001 * @param {Object} config The config object
31004 Roo.bootstrap.MasonryBrick = function(config){
31005 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
31011 * When a MasonryBrick is clcik
31012 * @param {Roo.bootstrap.MasonryBrick} this
31013 * @param {Roo.EventObject} e
31019 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
31022 * @cfg {String} title
31026 * @cfg {String} html
31030 * @cfg {String} bgimage
31034 * @cfg {String} videourl
31038 * @cfg {String} cls
31042 * @cfg {String} href
31046 * @cfg {String} (xs|sm|md|md-left|md-right|tall|wide) size
31051 * @cfg {String} (center|bottom) placetitle
31056 * @cfg {Boolean} isFitContainer defalut true
31058 isFitContainer : true,
31061 * @cfg {Boolean} preventDefault defalut false
31063 preventDefault : false,
31065 getAutoCreate : function()
31067 if(!this.isFitContainer){
31068 return this.getSplitAutoCreate();
31071 var cls = 'masonry-brick masonry-brick-full';
31073 if(this.href.length){
31074 cls += ' masonry-brick-link';
31077 if(this.bgimage.length){
31078 cls += ' masonry-brick-image';
31081 if(!this.html.length){
31082 cls += ' enable-mask';
31086 cls += ' masonry-' + this.size + '-brick';
31089 if(this.placetitle.length){
31091 switch (this.placetitle) {
31093 cls += ' masonry-center-title';
31096 cls += ' masonry-bottom-title';
31103 if(!this.html.length && !this.bgimage.length){
31104 cls += ' masonry-center-title';
31107 if(!this.html.length && this.bgimage.length){
31108 cls += ' masonry-bottom-title';
31113 cls += ' ' + this.cls;
31117 tag: (this.href.length) ? 'a' : 'div',
31122 cls: 'masonry-brick-paragraph',
31128 if(this.href.length){
31129 cfg.href = this.href;
31132 var cn = cfg.cn[0].cn;
31134 if(this.title.length){
31137 cls: 'masonry-brick-title',
31142 if(this.html.length){
31145 cls: 'masonry-brick-text',
31149 if (!this.title.length && !this.html.length) {
31150 cfg.cn[0].cls += ' hide';
31153 if(this.bgimage.length){
31156 cls: 'masonry-brick-image-view',
31161 if(this.videourl.length){
31162 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
31163 // youtube support only?
31166 cls: 'masonry-brick-image-view',
31169 allowfullscreen : true
31177 cls: 'masonry-brick-mask'
31184 getSplitAutoCreate : function()
31186 var cls = 'masonry-brick masonry-brick-split';
31188 if(this.href.length){
31189 cls += ' masonry-brick-link';
31192 if(this.bgimage.length){
31193 cls += ' masonry-brick-image';
31197 cls += ' masonry-' + this.size + '-brick';
31200 switch (this.placetitle) {
31202 cls += ' masonry-center-title';
31205 cls += ' masonry-bottom-title';
31208 if(!this.bgimage.length){
31209 cls += ' masonry-center-title';
31212 if(this.bgimage.length){
31213 cls += ' masonry-bottom-title';
31219 cls += ' ' + this.cls;
31223 tag: (this.href.length) ? 'a' : 'div',
31228 cls: 'masonry-brick-split-head',
31232 cls: 'masonry-brick-paragraph',
31239 cls: 'masonry-brick-split-body',
31245 if(this.href.length){
31246 cfg.href = this.href;
31249 if(this.title.length){
31250 cfg.cn[0].cn[0].cn.push({
31252 cls: 'masonry-brick-title',
31257 if(this.html.length){
31258 cfg.cn[1].cn.push({
31260 cls: 'masonry-brick-text',
31265 if(this.bgimage.length){
31266 cfg.cn[0].cn.push({
31268 cls: 'masonry-brick-image-view',
31273 if(this.videourl.length){
31274 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
31275 // youtube support only?
31276 cfg.cn[0].cn.cn.push({
31278 cls: 'masonry-brick-image-view',
31281 allowfullscreen : true
31288 initEvents: function()
31290 switch (this.size) {
31323 this.el.on('touchstart', this.onTouchStart, this);
31324 this.el.on('touchmove', this.onTouchMove, this);
31325 this.el.on('touchend', this.onTouchEnd, this);
31326 this.el.on('contextmenu', this.onContextMenu, this);
31328 this.el.on('mouseenter' ,this.enter, this);
31329 this.el.on('mouseleave', this.leave, this);
31330 this.el.on('click', this.onClick, this);
31333 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
31334 this.parent().bricks.push(this);
31339 onClick: function(e, el)
31341 var time = this.endTimer - this.startTimer;
31345 e.preventDefault();
31350 if(!this.preventDefault){
31354 e.preventDefault();
31355 this.fireEvent('click', this);
31358 enter: function(e, el)
31360 e.preventDefault();
31362 if(!this.isFitContainer){
31366 if(this.bgimage.length && this.html.length){
31367 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
31371 leave: function(e, el)
31373 e.preventDefault();
31375 if(!this.isFitContainer){
31379 if(this.bgimage.length && this.html.length){
31380 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
31384 onTouchStart: function(e, el)
31386 // e.preventDefault();
31388 this.touchmoved = false;
31390 if(!this.isFitContainer){
31394 if(!this.bgimage.length || !this.html.length){
31398 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
31400 this.timer = new Date().getTime();
31404 onTouchMove: function(e, el)
31406 this.touchmoved = true;
31409 onContextMenu : function(e,el)
31411 e.preventDefault();
31412 e.stopPropagation();
31416 onTouchEnd: function(e, el)
31418 // e.preventDefault();
31420 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
31427 if(!this.bgimage.length || !this.html.length){
31429 if(this.href.length){
31430 window.location.href = this.href;
31436 if(!this.isFitContainer){
31440 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
31442 window.location.href = this.href;
31457 * @class Roo.bootstrap.Brick
31458 * @extends Roo.bootstrap.Component
31459 * Bootstrap Brick class
31462 * Create a new Brick
31463 * @param {Object} config The config object
31466 Roo.bootstrap.Brick = function(config){
31467 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
31473 * When a Brick is click
31474 * @param {Roo.bootstrap.Brick} this
31475 * @param {Roo.EventObject} e
31481 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
31484 * @cfg {String} title
31488 * @cfg {String} html
31492 * @cfg {String} bgimage
31496 * @cfg {String} cls
31500 * @cfg {String} href
31504 * @cfg {String} video
31508 * @cfg {Boolean} square
31512 getAutoCreate : function()
31514 var cls = 'roo-brick';
31516 if(this.href.length){
31517 cls += ' roo-brick-link';
31520 if(this.bgimage.length){
31521 cls += ' roo-brick-image';
31524 if(!this.html.length && !this.bgimage.length){
31525 cls += ' roo-brick-center-title';
31528 if(!this.html.length && this.bgimage.length){
31529 cls += ' roo-brick-bottom-title';
31533 cls += ' ' + this.cls;
31537 tag: (this.href.length) ? 'a' : 'div',
31542 cls: 'roo-brick-paragraph',
31548 if(this.href.length){
31549 cfg.href = this.href;
31552 var cn = cfg.cn[0].cn;
31554 if(this.title.length){
31557 cls: 'roo-brick-title',
31562 if(this.html.length){
31565 cls: 'roo-brick-text',
31572 if(this.bgimage.length){
31575 cls: 'roo-brick-image-view',
31583 initEvents: function()
31585 if(this.title.length || this.html.length){
31586 this.el.on('mouseenter' ,this.enter, this);
31587 this.el.on('mouseleave', this.leave, this);
31591 Roo.EventManager.onWindowResize(this.resize, this);
31596 resize : function()
31598 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
31600 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
31602 if(this.bgimage.length){
31603 var image = this.el.select('.roo-brick-image-view', true).first();
31604 image.setWidth(paragraph.getWidth());
31605 image.setHeight(paragraph.getWidth());
31607 this.el.setHeight(paragraph.getWidth());
31613 enter: function(e, el)
31615 e.preventDefault();
31617 if(this.bgimage.length){
31618 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
31619 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
31623 leave: function(e, el)
31625 e.preventDefault();
31627 if(this.bgimage.length){
31628 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
31629 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
31645 * @class Roo.bootstrap.NumberField
31646 * @extends Roo.bootstrap.Input
31647 * Bootstrap NumberField class
31653 * Create a new NumberField
31654 * @param {Object} config The config object
31657 Roo.bootstrap.NumberField = function(config){
31658 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
31661 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
31664 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
31666 allowDecimals : true,
31668 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
31670 decimalSeparator : ".",
31672 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
31674 decimalPrecision : 2,
31676 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
31678 allowNegative : true,
31680 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
31682 minValue : Number.NEGATIVE_INFINITY,
31684 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
31686 maxValue : Number.MAX_VALUE,
31688 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
31690 minText : "The minimum value for this field is {0}",
31692 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
31694 maxText : "The maximum value for this field is {0}",
31696 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
31697 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
31699 nanText : "{0} is not a valid number",
31701 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
31706 initEvents : function()
31708 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
31710 var allowed = "0123456789";
31712 if(this.allowDecimals){
31713 allowed += this.decimalSeparator;
31716 if(this.allowNegative){
31720 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
31722 var keyPress = function(e){
31724 var k = e.getKey();
31726 var c = e.getCharCode();
31728 if(k == e.MINUS && allowed.indexOf(String.fromCharCode(c)) === -1){ // coz minus is mask as specialKey...
31733 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
31737 if(allowed.indexOf(String.fromCharCode(c)) === -1){
31742 this.el.on("keypress", keyPress, this);
31745 validateValue : function(value)
31748 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
31752 var num = this.parseValue(value);
31755 this.markInvalid(String.format(this.nanText, value));
31759 if(num < this.minValue){
31760 this.markInvalid(String.format(this.minText, this.minValue));
31764 if(num > this.maxValue){
31765 this.markInvalid(String.format(this.maxText, this.maxValue));
31772 getValue : function()
31774 return this.fixPrecision(this.parseValue(Roo.bootstrap.NumberField.superclass.getValue.call(this)));
31777 parseValue : function(value)
31779 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
31780 return isNaN(value) ? '' : value;
31783 fixPrecision : function(value)
31785 var nan = isNaN(value);
31787 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
31788 return nan ? '' : value;
31790 return parseFloat(value).toFixed(this.decimalPrecision);
31793 setValue : function(v)
31795 v = this.fixPrecision(v);
31796 Roo.bootstrap.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
31799 decimalPrecisionFcn : function(v)
31801 return Math.floor(v);
31804 beforeBlur : function()
31810 var v = this.parseValue(this.getRawValue());
31821 * Ext JS Library 1.1.1
31822 * Copyright(c) 2006-2007, Ext JS, LLC.
31824 * Originally Released Under LGPL - original licence link has changed is not relivant.
31827 * <script type="text/javascript">
31832 * @class Roo.bootstrap.SplitBar
31833 * @extends Roo.util.Observable
31834 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
31838 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
31839 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
31840 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
31841 split.minSize = 100;
31842 split.maxSize = 600;
31843 split.animate = true;
31844 split.on('moved', splitterMoved);
31847 * Create a new SplitBar
31848 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
31849 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
31850 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
31851 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
31852 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
31853 position of the SplitBar).
31855 Roo.bootstrap.SplitBar = function(cfg){
31860 // dragElement : elm
31861 // resizingElement: el,
31863 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
31864 // placement : Roo.bootstrap.SplitBar.LEFT ,
31865 // existingProxy ???
31868 this.el = Roo.get(cfg.dragElement, true);
31869 this.el.dom.unselectable = "on";
31871 this.resizingEl = Roo.get(cfg.resizingElement, true);
31875 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
31876 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
31879 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
31882 * The minimum size of the resizing element. (Defaults to 0)
31888 * The maximum size of the resizing element. (Defaults to 2000)
31891 this.maxSize = 2000;
31894 * Whether to animate the transition to the new size
31897 this.animate = false;
31900 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
31903 this.useShim = false;
31908 if(!cfg.existingProxy){
31910 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
31912 this.proxy = Roo.get(cfg.existingProxy).dom;
31915 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
31918 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
31921 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
31924 this.dragSpecs = {};
31927 * @private The adapter to use to positon and resize elements
31929 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
31930 this.adapter.init(this);
31932 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31934 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
31935 this.el.addClass("roo-splitbar-h");
31938 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
31939 this.el.addClass("roo-splitbar-v");
31945 * Fires when the splitter is moved (alias for {@link #event-moved})
31946 * @param {Roo.bootstrap.SplitBar} this
31947 * @param {Number} newSize the new width or height
31952 * Fires when the splitter is moved
31953 * @param {Roo.bootstrap.SplitBar} this
31954 * @param {Number} newSize the new width or height
31958 * @event beforeresize
31959 * Fires before the splitter is dragged
31960 * @param {Roo.bootstrap.SplitBar} this
31962 "beforeresize" : true,
31964 "beforeapply" : true
31967 Roo.util.Observable.call(this);
31970 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
31971 onStartProxyDrag : function(x, y){
31972 this.fireEvent("beforeresize", this);
31974 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
31976 o.enableDisplayMode("block");
31977 // all splitbars share the same overlay
31978 Roo.bootstrap.SplitBar.prototype.overlay = o;
31980 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
31981 this.overlay.show();
31982 Roo.get(this.proxy).setDisplayed("block");
31983 var size = this.adapter.getElementSize(this);
31984 this.activeMinSize = this.getMinimumSize();;
31985 this.activeMaxSize = this.getMaximumSize();;
31986 var c1 = size - this.activeMinSize;
31987 var c2 = Math.max(this.activeMaxSize - size, 0);
31988 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31989 this.dd.resetConstraints();
31990 this.dd.setXConstraint(
31991 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
31992 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
31994 this.dd.setYConstraint(0, 0);
31996 this.dd.resetConstraints();
31997 this.dd.setXConstraint(0, 0);
31998 this.dd.setYConstraint(
31999 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
32000 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
32003 this.dragSpecs.startSize = size;
32004 this.dragSpecs.startPoint = [x, y];
32005 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
32009 * @private Called after the drag operation by the DDProxy
32011 onEndProxyDrag : function(e){
32012 Roo.get(this.proxy).setDisplayed(false);
32013 var endPoint = Roo.lib.Event.getXY(e);
32015 this.overlay.hide();
32018 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32019 newSize = this.dragSpecs.startSize +
32020 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
32021 endPoint[0] - this.dragSpecs.startPoint[0] :
32022 this.dragSpecs.startPoint[0] - endPoint[0]
32025 newSize = this.dragSpecs.startSize +
32026 (this.placement == Roo.bootstrap.SplitBar.TOP ?
32027 endPoint[1] - this.dragSpecs.startPoint[1] :
32028 this.dragSpecs.startPoint[1] - endPoint[1]
32031 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
32032 if(newSize != this.dragSpecs.startSize){
32033 if(this.fireEvent('beforeapply', this, newSize) !== false){
32034 this.adapter.setElementSize(this, newSize);
32035 this.fireEvent("moved", this, newSize);
32036 this.fireEvent("resize", this, newSize);
32042 * Get the adapter this SplitBar uses
32043 * @return The adapter object
32045 getAdapter : function(){
32046 return this.adapter;
32050 * Set the adapter this SplitBar uses
32051 * @param {Object} adapter A SplitBar adapter object
32053 setAdapter : function(adapter){
32054 this.adapter = adapter;
32055 this.adapter.init(this);
32059 * Gets the minimum size for the resizing element
32060 * @return {Number} The minimum size
32062 getMinimumSize : function(){
32063 return this.minSize;
32067 * Sets the minimum size for the resizing element
32068 * @param {Number} minSize The minimum size
32070 setMinimumSize : function(minSize){
32071 this.minSize = minSize;
32075 * Gets the maximum size for the resizing element
32076 * @return {Number} The maximum size
32078 getMaximumSize : function(){
32079 return this.maxSize;
32083 * Sets the maximum size for the resizing element
32084 * @param {Number} maxSize The maximum size
32086 setMaximumSize : function(maxSize){
32087 this.maxSize = maxSize;
32091 * Sets the initialize size for the resizing element
32092 * @param {Number} size The initial size
32094 setCurrentSize : function(size){
32095 var oldAnimate = this.animate;
32096 this.animate = false;
32097 this.adapter.setElementSize(this, size);
32098 this.animate = oldAnimate;
32102 * Destroy this splitbar.
32103 * @param {Boolean} removeEl True to remove the element
32105 destroy : function(removeEl){
32107 this.shim.remove();
32110 this.proxy.parentNode.removeChild(this.proxy);
32118 * @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.
32120 Roo.bootstrap.SplitBar.createProxy = function(dir){
32121 var proxy = new Roo.Element(document.createElement("div"));
32122 proxy.unselectable();
32123 var cls = 'roo-splitbar-proxy';
32124 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
32125 document.body.appendChild(proxy.dom);
32130 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
32131 * Default Adapter. It assumes the splitter and resizing element are not positioned
32132 * elements and only gets/sets the width of the element. Generally used for table based layouts.
32134 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
32137 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
32138 // do nothing for now
32139 init : function(s){
32143 * Called before drag operations to get the current size of the resizing element.
32144 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
32146 getElementSize : function(s){
32147 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32148 return s.resizingEl.getWidth();
32150 return s.resizingEl.getHeight();
32155 * Called after drag operations to set the size of the resizing element.
32156 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
32157 * @param {Number} newSize The new size to set
32158 * @param {Function} onComplete A function to be invoked when resizing is complete
32160 setElementSize : function(s, newSize, onComplete){
32161 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32163 s.resizingEl.setWidth(newSize);
32165 onComplete(s, newSize);
32168 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
32173 s.resizingEl.setHeight(newSize);
32175 onComplete(s, newSize);
32178 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
32185 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
32186 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
32187 * Adapter that moves the splitter element to align with the resized sizing element.
32188 * Used with an absolute positioned SplitBar.
32189 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
32190 * document.body, make sure you assign an id to the body element.
32192 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
32193 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
32194 this.container = Roo.get(container);
32197 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
32198 init : function(s){
32199 this.basic.init(s);
32202 getElementSize : function(s){
32203 return this.basic.getElementSize(s);
32206 setElementSize : function(s, newSize, onComplete){
32207 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
32210 moveSplitter : function(s){
32211 var yes = Roo.bootstrap.SplitBar;
32212 switch(s.placement){
32214 s.el.setX(s.resizingEl.getRight());
32217 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
32220 s.el.setY(s.resizingEl.getBottom());
32223 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
32230 * Orientation constant - Create a vertical SplitBar
32234 Roo.bootstrap.SplitBar.VERTICAL = 1;
32237 * Orientation constant - Create a horizontal SplitBar
32241 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
32244 * Placement constant - The resizing element is to the left of the splitter element
32248 Roo.bootstrap.SplitBar.LEFT = 1;
32251 * Placement constant - The resizing element is to the right of the splitter element
32255 Roo.bootstrap.SplitBar.RIGHT = 2;
32258 * Placement constant - The resizing element is positioned above the splitter element
32262 Roo.bootstrap.SplitBar.TOP = 3;
32265 * Placement constant - The resizing element is positioned under splitter element
32269 Roo.bootstrap.SplitBar.BOTTOM = 4;
32270 Roo.namespace("Roo.bootstrap.layout");/*
32272 * Ext JS Library 1.1.1
32273 * Copyright(c) 2006-2007, Ext JS, LLC.
32275 * Originally Released Under LGPL - original licence link has changed is not relivant.
32278 * <script type="text/javascript">
32282 * @class Roo.bootstrap.layout.Manager
32283 * @extends Roo.bootstrap.Component
32284 * Base class for layout managers.
32286 Roo.bootstrap.layout.Manager = function(config)
32288 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
32294 /** false to disable window resize monitoring @type Boolean */
32295 this.monitorWindowResize = true;
32300 * Fires when a layout is performed.
32301 * @param {Roo.LayoutManager} this
32305 * @event regionresized
32306 * Fires when the user resizes a region.
32307 * @param {Roo.LayoutRegion} region The resized region
32308 * @param {Number} newSize The new size (width for east/west, height for north/south)
32310 "regionresized" : true,
32312 * @event regioncollapsed
32313 * Fires when a region is collapsed.
32314 * @param {Roo.LayoutRegion} region The collapsed region
32316 "regioncollapsed" : true,
32318 * @event regionexpanded
32319 * Fires when a region is expanded.
32320 * @param {Roo.LayoutRegion} region The expanded region
32322 "regionexpanded" : true
32324 this.updating = false;
32327 this.el = Roo.get(config.el);
32333 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
32338 monitorWindowResize : true,
32344 onRender : function(ct, position)
32347 this.el = Roo.get(ct);
32350 //this.fireEvent('render',this);
32354 initEvents: function()
32358 // ie scrollbar fix
32359 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
32360 document.body.scroll = "no";
32361 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
32362 this.el.position('relative');
32364 this.id = this.el.id;
32365 this.el.addClass("roo-layout-container");
32366 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
32367 if(this.el.dom != document.body ) {
32368 this.el.on('resize', this.layout,this);
32369 this.el.on('show', this.layout,this);
32375 * Returns true if this layout is currently being updated
32376 * @return {Boolean}
32378 isUpdating : function(){
32379 return this.updating;
32383 * Suspend the LayoutManager from doing auto-layouts while
32384 * making multiple add or remove calls
32386 beginUpdate : function(){
32387 this.updating = true;
32391 * Restore auto-layouts and optionally disable the manager from performing a layout
32392 * @param {Boolean} noLayout true to disable a layout update
32394 endUpdate : function(noLayout){
32395 this.updating = false;
32401 layout: function(){
32405 onRegionResized : function(region, newSize){
32406 this.fireEvent("regionresized", region, newSize);
32410 onRegionCollapsed : function(region){
32411 this.fireEvent("regioncollapsed", region);
32414 onRegionExpanded : function(region){
32415 this.fireEvent("regionexpanded", region);
32419 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
32420 * performs box-model adjustments.
32421 * @return {Object} The size as an object {width: (the width), height: (the height)}
32423 getViewSize : function()
32426 if(this.el.dom != document.body){
32427 size = this.el.getSize();
32429 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
32431 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
32432 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
32437 * Returns the Element this layout is bound to.
32438 * @return {Roo.Element}
32440 getEl : function(){
32445 * Returns the specified region.
32446 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
32447 * @return {Roo.LayoutRegion}
32449 getRegion : function(target){
32450 return this.regions[target.toLowerCase()];
32453 onWindowResize : function(){
32454 if(this.monitorWindowResize){
32461 * Ext JS Library 1.1.1
32462 * Copyright(c) 2006-2007, Ext JS, LLC.
32464 * Originally Released Under LGPL - original licence link has changed is not relivant.
32467 * <script type="text/javascript">
32470 * @class Roo.bootstrap.layout.Border
32471 * @extends Roo.bootstrap.layout.Manager
32472 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
32473 * please see: examples/bootstrap/nested.html<br><br>
32475 <b>The container the layout is rendered into can be either the body element or any other element.
32476 If it is not the body element, the container needs to either be an absolute positioned element,
32477 or you will need to add "position:relative" to the css of the container. You will also need to specify
32478 the container size if it is not the body element.</b>
32481 * Create a new Border
32482 * @param {Object} config Configuration options
32484 Roo.bootstrap.layout.Border = function(config){
32485 config = config || {};
32486 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
32490 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
32491 if(config[region]){
32492 config[region].region = region;
32493 this.addRegion(config[region]);
32499 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
32501 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
32503 * Creates and adds a new region if it doesn't already exist.
32504 * @param {String} target The target region key (north, south, east, west or center).
32505 * @param {Object} config The regions config object
32506 * @return {BorderLayoutRegion} The new region
32508 addRegion : function(config)
32510 if(!this.regions[config.region]){
32511 var r = this.factory(config);
32512 this.bindRegion(r);
32514 return this.regions[config.region];
32518 bindRegion : function(r){
32519 this.regions[r.config.region] = r;
32521 r.on("visibilitychange", this.layout, this);
32522 r.on("paneladded", this.layout, this);
32523 r.on("panelremoved", this.layout, this);
32524 r.on("invalidated", this.layout, this);
32525 r.on("resized", this.onRegionResized, this);
32526 r.on("collapsed", this.onRegionCollapsed, this);
32527 r.on("expanded", this.onRegionExpanded, this);
32531 * Performs a layout update.
32533 layout : function()
32535 if(this.updating) {
32539 // render all the rebions if they have not been done alreayd?
32540 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
32541 if(this.regions[region] && !this.regions[region].bodyEl){
32542 this.regions[region].onRender(this.el)
32546 var size = this.getViewSize();
32547 var w = size.width;
32548 var h = size.height;
32553 //var x = 0, y = 0;
32555 var rs = this.regions;
32556 var north = rs["north"];
32557 var south = rs["south"];
32558 var west = rs["west"];
32559 var east = rs["east"];
32560 var center = rs["center"];
32561 //if(this.hideOnLayout){ // not supported anymore
32562 //c.el.setStyle("display", "none");
32564 if(north && north.isVisible()){
32565 var b = north.getBox();
32566 var m = north.getMargins();
32567 b.width = w - (m.left+m.right);
32570 centerY = b.height + b.y + m.bottom;
32571 centerH -= centerY;
32572 north.updateBox(this.safeBox(b));
32574 if(south && south.isVisible()){
32575 var b = south.getBox();
32576 var m = south.getMargins();
32577 b.width = w - (m.left+m.right);
32579 var totalHeight = (b.height + m.top + m.bottom);
32580 b.y = h - totalHeight + m.top;
32581 centerH -= totalHeight;
32582 south.updateBox(this.safeBox(b));
32584 if(west && west.isVisible()){
32585 var b = west.getBox();
32586 var m = west.getMargins();
32587 b.height = centerH - (m.top+m.bottom);
32589 b.y = centerY + m.top;
32590 var totalWidth = (b.width + m.left + m.right);
32591 centerX += totalWidth;
32592 centerW -= totalWidth;
32593 west.updateBox(this.safeBox(b));
32595 if(east && east.isVisible()){
32596 var b = east.getBox();
32597 var m = east.getMargins();
32598 b.height = centerH - (m.top+m.bottom);
32599 var totalWidth = (b.width + m.left + m.right);
32600 b.x = w - totalWidth + m.left;
32601 b.y = centerY + m.top;
32602 centerW -= totalWidth;
32603 east.updateBox(this.safeBox(b));
32606 var m = center.getMargins();
32608 x: centerX + m.left,
32609 y: centerY + m.top,
32610 width: centerW - (m.left+m.right),
32611 height: centerH - (m.top+m.bottom)
32613 //if(this.hideOnLayout){
32614 //center.el.setStyle("display", "block");
32616 center.updateBox(this.safeBox(centerBox));
32619 this.fireEvent("layout", this);
32623 safeBox : function(box){
32624 box.width = Math.max(0, box.width);
32625 box.height = Math.max(0, box.height);
32630 * Adds a ContentPanel (or subclass) to this layout.
32631 * @param {String} target The target region key (north, south, east, west or center).
32632 * @param {Roo.ContentPanel} panel The panel to add
32633 * @return {Roo.ContentPanel} The added panel
32635 add : function(target, panel){
32637 target = target.toLowerCase();
32638 return this.regions[target].add(panel);
32642 * Remove a ContentPanel (or subclass) to this layout.
32643 * @param {String} target The target region key (north, south, east, west or center).
32644 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
32645 * @return {Roo.ContentPanel} The removed panel
32647 remove : function(target, panel){
32648 target = target.toLowerCase();
32649 return this.regions[target].remove(panel);
32653 * Searches all regions for a panel with the specified id
32654 * @param {String} panelId
32655 * @return {Roo.ContentPanel} The panel or null if it wasn't found
32657 findPanel : function(panelId){
32658 var rs = this.regions;
32659 for(var target in rs){
32660 if(typeof rs[target] != "function"){
32661 var p = rs[target].getPanel(panelId);
32671 * Searches all regions for a panel with the specified id and activates (shows) it.
32672 * @param {String/ContentPanel} panelId The panels id or the panel itself
32673 * @return {Roo.ContentPanel} The shown panel or null
32675 showPanel : function(panelId) {
32676 var rs = this.regions;
32677 for(var target in rs){
32678 var r = rs[target];
32679 if(typeof r != "function"){
32680 if(r.hasPanel(panelId)){
32681 return r.showPanel(panelId);
32689 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
32690 * @param {Roo.state.Provider} provider (optional) An alternate state provider
32693 restoreState : function(provider){
32695 provider = Roo.state.Manager;
32697 var sm = new Roo.LayoutStateManager();
32698 sm.init(this, provider);
32704 * Adds a xtype elements to the layout.
32708 xtype : 'ContentPanel',
32715 xtype : 'NestedLayoutPanel',
32721 items : [ ... list of content panels or nested layout panels.. ]
32725 * @param {Object} cfg Xtype definition of item to add.
32727 addxtype : function(cfg)
32729 // basically accepts a pannel...
32730 // can accept a layout region..!?!?
32731 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
32734 // theory? children can only be panels??
32736 //if (!cfg.xtype.match(/Panel$/)) {
32741 if (typeof(cfg.region) == 'undefined') {
32742 Roo.log("Failed to add Panel, region was not set");
32746 var region = cfg.region;
32752 xitems = cfg.items;
32759 case 'Content': // ContentPanel (el, cfg)
32760 case 'Scroll': // ContentPanel (el, cfg)
32762 cfg.autoCreate = true;
32763 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
32765 // var el = this.el.createChild();
32766 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
32769 this.add(region, ret);
32773 case 'TreePanel': // our new panel!
32774 cfg.el = this.el.createChild();
32775 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32776 this.add(region, ret);
32781 // create a new Layout (which is a Border Layout...
32783 var clayout = cfg.layout;
32784 clayout.el = this.el.createChild();
32785 clayout.items = clayout.items || [];
32789 // replace this exitems with the clayout ones..
32790 xitems = clayout.items;
32792 // force background off if it's in center...
32793 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
32794 cfg.background = false;
32796 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
32799 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
32800 //console.log('adding nested layout panel ' + cfg.toSource());
32801 this.add(region, ret);
32802 nb = {}; /// find first...
32807 // needs grid and region
32809 //var el = this.getRegion(region).el.createChild();
32811 *var el = this.el.createChild();
32812 // create the grid first...
32813 cfg.grid.container = el;
32814 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
32817 if (region == 'center' && this.active ) {
32818 cfg.background = false;
32821 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
32823 this.add(region, ret);
32825 if (cfg.background) {
32826 // render grid on panel activation (if panel background)
32827 ret.on('activate', function(gp) {
32828 if (!gp.grid.rendered) {
32829 // gp.grid.render(el);
32833 // cfg.grid.render(el);
32839 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
32840 // it was the old xcomponent building that caused this before.
32841 // espeically if border is the top element in the tree.
32851 if (typeof(Roo[cfg.xtype]) != 'undefined') {
32853 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32854 this.add(region, ret);
32858 throw "Can not add '" + cfg.xtype + "' to Border";
32864 this.beginUpdate();
32868 Roo.each(xitems, function(i) {
32869 region = nb && i.region ? i.region : false;
32871 var add = ret.addxtype(i);
32874 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
32875 if (!i.background) {
32876 abn[region] = nb[region] ;
32883 // make the last non-background panel active..
32884 //if (nb) { Roo.log(abn); }
32887 for(var r in abn) {
32888 region = this.getRegion(r);
32890 // tried using nb[r], but it does not work..
32892 region.showPanel(abn[r]);
32903 factory : function(cfg)
32906 var validRegions = Roo.bootstrap.layout.Border.regions;
32908 var target = cfg.region;
32911 var r = Roo.bootstrap.layout;
32915 return new r.North(cfg);
32917 return new r.South(cfg);
32919 return new r.East(cfg);
32921 return new r.West(cfg);
32923 return new r.Center(cfg);
32925 throw 'Layout region "'+target+'" not supported.';
32932 * Ext JS Library 1.1.1
32933 * Copyright(c) 2006-2007, Ext JS, LLC.
32935 * Originally Released Under LGPL - original licence link has changed is not relivant.
32938 * <script type="text/javascript">
32942 * @class Roo.bootstrap.layout.Basic
32943 * @extends Roo.util.Observable
32944 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
32945 * and does not have a titlebar, tabs or any other features. All it does is size and position
32946 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
32947 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
32948 * @cfg {string} region the region that it inhabits..
32949 * @cfg {bool} skipConfig skip config?
32953 Roo.bootstrap.layout.Basic = function(config){
32955 this.mgr = config.mgr;
32957 this.position = config.region;
32959 var skipConfig = config.skipConfig;
32963 * @scope Roo.BasicLayoutRegion
32967 * @event beforeremove
32968 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
32969 * @param {Roo.LayoutRegion} this
32970 * @param {Roo.ContentPanel} panel The panel
32971 * @param {Object} e The cancel event object
32973 "beforeremove" : true,
32975 * @event invalidated
32976 * Fires when the layout for this region is changed.
32977 * @param {Roo.LayoutRegion} this
32979 "invalidated" : true,
32981 * @event visibilitychange
32982 * Fires when this region is shown or hidden
32983 * @param {Roo.LayoutRegion} this
32984 * @param {Boolean} visibility true or false
32986 "visibilitychange" : true,
32988 * @event paneladded
32989 * Fires when a panel is added.
32990 * @param {Roo.LayoutRegion} this
32991 * @param {Roo.ContentPanel} panel The panel
32993 "paneladded" : true,
32995 * @event panelremoved
32996 * Fires when a panel is removed.
32997 * @param {Roo.LayoutRegion} this
32998 * @param {Roo.ContentPanel} panel The panel
33000 "panelremoved" : true,
33002 * @event beforecollapse
33003 * Fires when this region before collapse.
33004 * @param {Roo.LayoutRegion} this
33006 "beforecollapse" : true,
33009 * Fires when this region is collapsed.
33010 * @param {Roo.LayoutRegion} this
33012 "collapsed" : true,
33015 * Fires when this region is expanded.
33016 * @param {Roo.LayoutRegion} this
33021 * Fires when this region is slid into view.
33022 * @param {Roo.LayoutRegion} this
33024 "slideshow" : true,
33027 * Fires when this region slides out of view.
33028 * @param {Roo.LayoutRegion} this
33030 "slidehide" : true,
33032 * @event panelactivated
33033 * Fires when a panel is activated.
33034 * @param {Roo.LayoutRegion} this
33035 * @param {Roo.ContentPanel} panel The activated panel
33037 "panelactivated" : true,
33040 * Fires when the user resizes this region.
33041 * @param {Roo.LayoutRegion} this
33042 * @param {Number} newSize The new size (width for east/west, height for north/south)
33046 /** A collection of panels in this region. @type Roo.util.MixedCollection */
33047 this.panels = new Roo.util.MixedCollection();
33048 this.panels.getKey = this.getPanelId.createDelegate(this);
33050 this.activePanel = null;
33051 // ensure listeners are added...
33053 if (config.listeners || config.events) {
33054 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
33055 listeners : config.listeners || {},
33056 events : config.events || {}
33060 if(skipConfig !== true){
33061 this.applyConfig(config);
33065 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
33067 getPanelId : function(p){
33071 applyConfig : function(config){
33072 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33073 this.config = config;
33078 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
33079 * the width, for horizontal (north, south) the height.
33080 * @param {Number} newSize The new width or height
33082 resizeTo : function(newSize){
33083 var el = this.el ? this.el :
33084 (this.activePanel ? this.activePanel.getEl() : null);
33086 switch(this.position){
33089 el.setWidth(newSize);
33090 this.fireEvent("resized", this, newSize);
33094 el.setHeight(newSize);
33095 this.fireEvent("resized", this, newSize);
33101 getBox : function(){
33102 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
33105 getMargins : function(){
33106 return this.margins;
33109 updateBox : function(box){
33111 var el = this.activePanel.getEl();
33112 el.dom.style.left = box.x + "px";
33113 el.dom.style.top = box.y + "px";
33114 this.activePanel.setSize(box.width, box.height);
33118 * Returns the container element for this region.
33119 * @return {Roo.Element}
33121 getEl : function(){
33122 return this.activePanel;
33126 * Returns true if this region is currently visible.
33127 * @return {Boolean}
33129 isVisible : function(){
33130 return this.activePanel ? true : false;
33133 setActivePanel : function(panel){
33134 panel = this.getPanel(panel);
33135 if(this.activePanel && this.activePanel != panel){
33136 this.activePanel.setActiveState(false);
33137 this.activePanel.getEl().setLeftTop(-10000,-10000);
33139 this.activePanel = panel;
33140 panel.setActiveState(true);
33142 panel.setSize(this.box.width, this.box.height);
33144 this.fireEvent("panelactivated", this, panel);
33145 this.fireEvent("invalidated");
33149 * Show the specified panel.
33150 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
33151 * @return {Roo.ContentPanel} The shown panel or null
33153 showPanel : function(panel){
33154 panel = this.getPanel(panel);
33156 this.setActivePanel(panel);
33162 * Get the active panel for this region.
33163 * @return {Roo.ContentPanel} The active panel or null
33165 getActivePanel : function(){
33166 return this.activePanel;
33170 * Add the passed ContentPanel(s)
33171 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33172 * @return {Roo.ContentPanel} The panel added (if only one was added)
33174 add : function(panel){
33175 if(arguments.length > 1){
33176 for(var i = 0, len = arguments.length; i < len; i++) {
33177 this.add(arguments[i]);
33181 if(this.hasPanel(panel)){
33182 this.showPanel(panel);
33185 var el = panel.getEl();
33186 if(el.dom.parentNode != this.mgr.el.dom){
33187 this.mgr.el.dom.appendChild(el.dom);
33189 if(panel.setRegion){
33190 panel.setRegion(this);
33192 this.panels.add(panel);
33193 el.setStyle("position", "absolute");
33194 if(!panel.background){
33195 this.setActivePanel(panel);
33196 if(this.config.initialSize && this.panels.getCount()==1){
33197 this.resizeTo(this.config.initialSize);
33200 this.fireEvent("paneladded", this, panel);
33205 * Returns true if the panel is in this region.
33206 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33207 * @return {Boolean}
33209 hasPanel : function(panel){
33210 if(typeof panel == "object"){ // must be panel obj
33211 panel = panel.getId();
33213 return this.getPanel(panel) ? true : false;
33217 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33218 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33219 * @param {Boolean} preservePanel Overrides the config preservePanel option
33220 * @return {Roo.ContentPanel} The panel that was removed
33222 remove : function(panel, preservePanel){
33223 panel = this.getPanel(panel);
33228 this.fireEvent("beforeremove", this, panel, e);
33229 if(e.cancel === true){
33232 var panelId = panel.getId();
33233 this.panels.removeKey(panelId);
33238 * Returns the panel specified or null if it's not in this region.
33239 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33240 * @return {Roo.ContentPanel}
33242 getPanel : function(id){
33243 if(typeof id == "object"){ // must be panel obj
33246 return this.panels.get(id);
33250 * Returns this regions position (north/south/east/west/center).
33253 getPosition: function(){
33254 return this.position;
33258 * Ext JS Library 1.1.1
33259 * Copyright(c) 2006-2007, Ext JS, LLC.
33261 * Originally Released Under LGPL - original licence link has changed is not relivant.
33264 * <script type="text/javascript">
33268 * @class Roo.bootstrap.layout.Region
33269 * @extends Roo.bootstrap.layout.Basic
33270 * This class represents a region in a layout manager.
33272 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
33273 * @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})
33274 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
33275 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
33276 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
33277 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
33278 * @cfg {String} title The title for the region (overrides panel titles)
33279 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
33280 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
33281 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
33282 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
33283 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
33284 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
33285 * the space available, similar to FireFox 1.5 tabs (defaults to false)
33286 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
33287 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
33288 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
33290 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
33291 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
33292 * @cfg {Boolean} disableTabTips True to disable tab tooltips
33293 * @cfg {Number} width For East/West panels
33294 * @cfg {Number} height For North/South panels
33295 * @cfg {Boolean} split To show the splitter
33296 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
33298 * @cfg {string} cls Extra CSS classes to add to region
33300 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
33301 * @cfg {string} region the region that it inhabits..
33304 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
33305 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
33307 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
33308 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
33309 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
33311 Roo.bootstrap.layout.Region = function(config)
33313 this.applyConfig(config);
33315 var mgr = config.mgr;
33316 var pos = config.region;
33317 config.skipConfig = true;
33318 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
33321 this.onRender(mgr.el);
33324 this.visible = true;
33325 this.collapsed = false;
33326 this.unrendered_panels = [];
33329 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
33331 position: '', // set by wrapper (eg. north/south etc..)
33332 unrendered_panels : null, // unrendered panels.
33333 createBody : function(){
33334 /** This region's body element
33335 * @type Roo.Element */
33336 this.bodyEl = this.el.createChild({
33338 cls: "roo-layout-panel-body tab-content" // bootstrap added...
33342 onRender: function(ctr, pos)
33344 var dh = Roo.DomHelper;
33345 /** This region's container element
33346 * @type Roo.Element */
33347 this.el = dh.append(ctr.dom, {
33349 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
33351 /** This region's title element
33352 * @type Roo.Element */
33354 this.titleEl = dh.append(this.el.dom,
33357 unselectable: "on",
33358 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
33360 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
33361 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
33364 this.titleEl.enableDisplayMode();
33365 /** This region's title text element
33366 * @type HTMLElement */
33367 this.titleTextEl = this.titleEl.dom.firstChild;
33368 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
33370 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
33371 this.closeBtn.enableDisplayMode();
33372 this.closeBtn.on("click", this.closeClicked, this);
33373 this.closeBtn.hide();
33375 this.createBody(this.config);
33376 if(this.config.hideWhenEmpty){
33378 this.on("paneladded", this.validateVisibility, this);
33379 this.on("panelremoved", this.validateVisibility, this);
33381 if(this.autoScroll){
33382 this.bodyEl.setStyle("overflow", "auto");
33384 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
33386 //if(c.titlebar !== false){
33387 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
33388 this.titleEl.hide();
33390 this.titleEl.show();
33391 if(this.config.title){
33392 this.titleTextEl.innerHTML = this.config.title;
33396 if(this.config.collapsed){
33397 this.collapse(true);
33399 if(this.config.hidden){
33403 if (this.unrendered_panels && this.unrendered_panels.length) {
33404 for (var i =0;i< this.unrendered_panels.length; i++) {
33405 this.add(this.unrendered_panels[i]);
33407 this.unrendered_panels = null;
33413 applyConfig : function(c)
33416 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
33417 var dh = Roo.DomHelper;
33418 if(c.titlebar !== false){
33419 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
33420 this.collapseBtn.on("click", this.collapse, this);
33421 this.collapseBtn.enableDisplayMode();
33423 if(c.showPin === true || this.showPin){
33424 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
33425 this.stickBtn.enableDisplayMode();
33426 this.stickBtn.on("click", this.expand, this);
33427 this.stickBtn.hide();
33432 /** This region's collapsed element
33433 * @type Roo.Element */
33436 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
33437 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
33440 if(c.floatable !== false){
33441 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
33442 this.collapsedEl.on("click", this.collapseClick, this);
33445 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
33446 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
33447 id: "message", unselectable: "on", style:{"float":"left"}});
33448 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
33450 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
33451 this.expandBtn.on("click", this.expand, this);
33455 if(this.collapseBtn){
33456 this.collapseBtn.setVisible(c.collapsible == true);
33459 this.cmargins = c.cmargins || this.cmargins ||
33460 (this.position == "west" || this.position == "east" ?
33461 {top: 0, left: 2, right:2, bottom: 0} :
33462 {top: 2, left: 0, right:0, bottom: 2});
33464 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33467 this.bottomTabs = c.tabPosition != "top";
33469 this.autoScroll = c.autoScroll || false;
33474 this.duration = c.duration || .30;
33475 this.slideDuration = c.slideDuration || .45;
33480 * Returns true if this region is currently visible.
33481 * @return {Boolean}
33483 isVisible : function(){
33484 return this.visible;
33488 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
33489 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
33491 //setCollapsedTitle : function(title){
33492 // title = title || " ";
33493 // if(this.collapsedTitleTextEl){
33494 // this.collapsedTitleTextEl.innerHTML = title;
33498 getBox : function(){
33500 // if(!this.collapsed){
33501 b = this.el.getBox(false, true);
33503 // b = this.collapsedEl.getBox(false, true);
33508 getMargins : function(){
33509 return this.margins;
33510 //return this.collapsed ? this.cmargins : this.margins;
33513 highlight : function(){
33514 this.el.addClass("x-layout-panel-dragover");
33517 unhighlight : function(){
33518 this.el.removeClass("x-layout-panel-dragover");
33521 updateBox : function(box)
33523 if (!this.bodyEl) {
33524 return; // not rendered yet..
33528 if(!this.collapsed){
33529 this.el.dom.style.left = box.x + "px";
33530 this.el.dom.style.top = box.y + "px";
33531 this.updateBody(box.width, box.height);
33533 this.collapsedEl.dom.style.left = box.x + "px";
33534 this.collapsedEl.dom.style.top = box.y + "px";
33535 this.collapsedEl.setSize(box.width, box.height);
33538 this.tabs.autoSizeTabs();
33542 updateBody : function(w, h)
33545 this.el.setWidth(w);
33546 w -= this.el.getBorderWidth("rl");
33547 if(this.config.adjustments){
33548 w += this.config.adjustments[0];
33551 if(h !== null && h > 0){
33552 this.el.setHeight(h);
33553 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
33554 h -= this.el.getBorderWidth("tb");
33555 if(this.config.adjustments){
33556 h += this.config.adjustments[1];
33558 this.bodyEl.setHeight(h);
33560 h = this.tabs.syncHeight(h);
33563 if(this.panelSize){
33564 w = w !== null ? w : this.panelSize.width;
33565 h = h !== null ? h : this.panelSize.height;
33567 if(this.activePanel){
33568 var el = this.activePanel.getEl();
33569 w = w !== null ? w : el.getWidth();
33570 h = h !== null ? h : el.getHeight();
33571 this.panelSize = {width: w, height: h};
33572 this.activePanel.setSize(w, h);
33574 if(Roo.isIE && this.tabs){
33575 this.tabs.el.repaint();
33580 * Returns the container element for this region.
33581 * @return {Roo.Element}
33583 getEl : function(){
33588 * Hides this region.
33591 //if(!this.collapsed){
33592 this.el.dom.style.left = "-2000px";
33595 // this.collapsedEl.dom.style.left = "-2000px";
33596 // this.collapsedEl.hide();
33598 this.visible = false;
33599 this.fireEvent("visibilitychange", this, false);
33603 * Shows this region if it was previously hidden.
33606 //if(!this.collapsed){
33609 // this.collapsedEl.show();
33611 this.visible = true;
33612 this.fireEvent("visibilitychange", this, true);
33615 closeClicked : function(){
33616 if(this.activePanel){
33617 this.remove(this.activePanel);
33621 collapseClick : function(e){
33623 e.stopPropagation();
33626 e.stopPropagation();
33632 * Collapses this region.
33633 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
33636 collapse : function(skipAnim, skipCheck = false){
33637 if(this.collapsed) {
33641 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
33643 this.collapsed = true;
33645 this.split.el.hide();
33647 if(this.config.animate && skipAnim !== true){
33648 this.fireEvent("invalidated", this);
33649 this.animateCollapse();
33651 this.el.setLocation(-20000,-20000);
33653 this.collapsedEl.show();
33654 this.fireEvent("collapsed", this);
33655 this.fireEvent("invalidated", this);
33661 animateCollapse : function(){
33666 * Expands this region if it was previously collapsed.
33667 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
33668 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
33671 expand : function(e, skipAnim){
33673 e.stopPropagation();
33675 if(!this.collapsed || this.el.hasActiveFx()) {
33679 this.afterSlideIn();
33682 this.collapsed = false;
33683 if(this.config.animate && skipAnim !== true){
33684 this.animateExpand();
33688 this.split.el.show();
33690 this.collapsedEl.setLocation(-2000,-2000);
33691 this.collapsedEl.hide();
33692 this.fireEvent("invalidated", this);
33693 this.fireEvent("expanded", this);
33697 animateExpand : function(){
33701 initTabs : function()
33703 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
33705 var ts = new Roo.bootstrap.panel.Tabs({
33706 el: this.bodyEl.dom,
33707 tabPosition: this.bottomTabs ? 'bottom' : 'top',
33708 disableTooltips: this.config.disableTabTips,
33709 toolbar : this.config.toolbar
33712 if(this.config.hideTabs){
33713 ts.stripWrap.setDisplayed(false);
33716 ts.resizeTabs = this.config.resizeTabs === true;
33717 ts.minTabWidth = this.config.minTabWidth || 40;
33718 ts.maxTabWidth = this.config.maxTabWidth || 250;
33719 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
33720 ts.monitorResize = false;
33721 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
33722 ts.bodyEl.addClass('roo-layout-tabs-body');
33723 this.panels.each(this.initPanelAsTab, this);
33726 initPanelAsTab : function(panel){
33727 var ti = this.tabs.addTab(
33731 this.config.closeOnTab && panel.isClosable(),
33734 if(panel.tabTip !== undefined){
33735 ti.setTooltip(panel.tabTip);
33737 ti.on("activate", function(){
33738 this.setActivePanel(panel);
33741 if(this.config.closeOnTab){
33742 ti.on("beforeclose", function(t, e){
33744 this.remove(panel);
33748 panel.tabItem = ti;
33753 updatePanelTitle : function(panel, title)
33755 if(this.activePanel == panel){
33756 this.updateTitle(title);
33759 var ti = this.tabs.getTab(panel.getEl().id);
33761 if(panel.tabTip !== undefined){
33762 ti.setTooltip(panel.tabTip);
33767 updateTitle : function(title){
33768 if(this.titleTextEl && !this.config.title){
33769 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
33773 setActivePanel : function(panel)
33775 panel = this.getPanel(panel);
33776 if(this.activePanel && this.activePanel != panel){
33777 this.activePanel.setActiveState(false);
33779 this.activePanel = panel;
33780 panel.setActiveState(true);
33781 if(this.panelSize){
33782 panel.setSize(this.panelSize.width, this.panelSize.height);
33785 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
33787 this.updateTitle(panel.getTitle());
33789 this.fireEvent("invalidated", this);
33791 this.fireEvent("panelactivated", this, panel);
33795 * Shows the specified panel.
33796 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
33797 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
33799 showPanel : function(panel)
33801 panel = this.getPanel(panel);
33804 var tab = this.tabs.getTab(panel.getEl().id);
33805 if(tab.isHidden()){
33806 this.tabs.unhideTab(tab.id);
33810 this.setActivePanel(panel);
33817 * Get the active panel for this region.
33818 * @return {Roo.ContentPanel} The active panel or null
33820 getActivePanel : function(){
33821 return this.activePanel;
33824 validateVisibility : function(){
33825 if(this.panels.getCount() < 1){
33826 this.updateTitle(" ");
33827 this.closeBtn.hide();
33830 if(!this.isVisible()){
33837 * Adds the passed ContentPanel(s) to this region.
33838 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33839 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
33841 add : function(panel)
33843 if(arguments.length > 1){
33844 for(var i = 0, len = arguments.length; i < len; i++) {
33845 this.add(arguments[i]);
33850 // if we have not been rendered yet, then we can not really do much of this..
33851 if (!this.bodyEl) {
33852 this.unrendered_panels.push(panel);
33859 if(this.hasPanel(panel)){
33860 this.showPanel(panel);
33863 panel.setRegion(this);
33864 this.panels.add(panel);
33865 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
33866 // sinle panel - no tab...?? would it not be better to render it with the tabs,
33867 // and hide them... ???
33868 this.bodyEl.dom.appendChild(panel.getEl().dom);
33869 if(panel.background !== true){
33870 this.setActivePanel(panel);
33872 this.fireEvent("paneladded", this, panel);
33879 this.initPanelAsTab(panel);
33883 if(panel.background !== true){
33884 this.tabs.activate(panel.getEl().id);
33886 this.fireEvent("paneladded", this, panel);
33891 * Hides the tab for the specified panel.
33892 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33894 hidePanel : function(panel){
33895 if(this.tabs && (panel = this.getPanel(panel))){
33896 this.tabs.hideTab(panel.getEl().id);
33901 * Unhides the tab for a previously hidden panel.
33902 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33904 unhidePanel : function(panel){
33905 if(this.tabs && (panel = this.getPanel(panel))){
33906 this.tabs.unhideTab(panel.getEl().id);
33910 clearPanels : function(){
33911 while(this.panels.getCount() > 0){
33912 this.remove(this.panels.first());
33917 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33918 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33919 * @param {Boolean} preservePanel Overrides the config preservePanel option
33920 * @return {Roo.ContentPanel} The panel that was removed
33922 remove : function(panel, preservePanel)
33924 panel = this.getPanel(panel);
33929 this.fireEvent("beforeremove", this, panel, e);
33930 if(e.cancel === true){
33933 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
33934 var panelId = panel.getId();
33935 this.panels.removeKey(panelId);
33937 document.body.appendChild(panel.getEl().dom);
33940 this.tabs.removeTab(panel.getEl().id);
33941 }else if (!preservePanel){
33942 this.bodyEl.dom.removeChild(panel.getEl().dom);
33944 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
33945 var p = this.panels.first();
33946 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
33947 tempEl.appendChild(p.getEl().dom);
33948 this.bodyEl.update("");
33949 this.bodyEl.dom.appendChild(p.getEl().dom);
33951 this.updateTitle(p.getTitle());
33953 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
33954 this.setActivePanel(p);
33956 panel.setRegion(null);
33957 if(this.activePanel == panel){
33958 this.activePanel = null;
33960 if(this.config.autoDestroy !== false && preservePanel !== true){
33961 try{panel.destroy();}catch(e){}
33963 this.fireEvent("panelremoved", this, panel);
33968 * Returns the TabPanel component used by this region
33969 * @return {Roo.TabPanel}
33971 getTabs : function(){
33975 createTool : function(parentEl, className){
33976 var btn = Roo.DomHelper.append(parentEl, {
33978 cls: "x-layout-tools-button",
33981 cls: "roo-layout-tools-button-inner " + className,
33985 btn.addClassOnOver("roo-layout-tools-button-over");
33990 * Ext JS Library 1.1.1
33991 * Copyright(c) 2006-2007, Ext JS, LLC.
33993 * Originally Released Under LGPL - original licence link has changed is not relivant.
33996 * <script type="text/javascript">
34002 * @class Roo.SplitLayoutRegion
34003 * @extends Roo.LayoutRegion
34004 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
34006 Roo.bootstrap.layout.Split = function(config){
34007 this.cursor = config.cursor;
34008 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
34011 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
34013 splitTip : "Drag to resize.",
34014 collapsibleSplitTip : "Drag to resize. Double click to hide.",
34015 useSplitTips : false,
34017 applyConfig : function(config){
34018 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
34021 onRender : function(ctr,pos) {
34023 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
34024 if(!this.config.split){
34029 var splitEl = Roo.DomHelper.append(ctr.dom, {
34031 id: this.el.id + "-split",
34032 cls: "roo-layout-split roo-layout-split-"+this.position,
34035 /** The SplitBar for this region
34036 * @type Roo.SplitBar */
34037 // does not exist yet...
34038 Roo.log([this.position, this.orientation]);
34040 this.split = new Roo.bootstrap.SplitBar({
34041 dragElement : splitEl,
34042 resizingElement: this.el,
34043 orientation : this.orientation
34046 this.split.on("moved", this.onSplitMove, this);
34047 this.split.useShim = this.config.useShim === true;
34048 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
34049 if(this.useSplitTips){
34050 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
34052 //if(config.collapsible){
34053 // this.split.el.on("dblclick", this.collapse, this);
34056 if(typeof this.config.minSize != "undefined"){
34057 this.split.minSize = this.config.minSize;
34059 if(typeof this.config.maxSize != "undefined"){
34060 this.split.maxSize = this.config.maxSize;
34062 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
34063 this.hideSplitter();
34068 getHMaxSize : function(){
34069 var cmax = this.config.maxSize || 10000;
34070 var center = this.mgr.getRegion("center");
34071 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
34074 getVMaxSize : function(){
34075 var cmax = this.config.maxSize || 10000;
34076 var center = this.mgr.getRegion("center");
34077 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
34080 onSplitMove : function(split, newSize){
34081 this.fireEvent("resized", this, newSize);
34085 * Returns the {@link Roo.SplitBar} for this region.
34086 * @return {Roo.SplitBar}
34088 getSplitBar : function(){
34093 this.hideSplitter();
34094 Roo.bootstrap.layout.Split.superclass.hide.call(this);
34097 hideSplitter : function(){
34099 this.split.el.setLocation(-2000,-2000);
34100 this.split.el.hide();
34106 this.split.el.show();
34108 Roo.bootstrap.layout.Split.superclass.show.call(this);
34111 beforeSlide: function(){
34112 if(Roo.isGecko){// firefox overflow auto bug workaround
34113 this.bodyEl.clip();
34115 this.tabs.bodyEl.clip();
34117 if(this.activePanel){
34118 this.activePanel.getEl().clip();
34120 if(this.activePanel.beforeSlide){
34121 this.activePanel.beforeSlide();
34127 afterSlide : function(){
34128 if(Roo.isGecko){// firefox overflow auto bug workaround
34129 this.bodyEl.unclip();
34131 this.tabs.bodyEl.unclip();
34133 if(this.activePanel){
34134 this.activePanel.getEl().unclip();
34135 if(this.activePanel.afterSlide){
34136 this.activePanel.afterSlide();
34142 initAutoHide : function(){
34143 if(this.autoHide !== false){
34144 if(!this.autoHideHd){
34145 var st = new Roo.util.DelayedTask(this.slideIn, this);
34146 this.autoHideHd = {
34147 "mouseout": function(e){
34148 if(!e.within(this.el, true)){
34152 "mouseover" : function(e){
34158 this.el.on(this.autoHideHd);
34162 clearAutoHide : function(){
34163 if(this.autoHide !== false){
34164 this.el.un("mouseout", this.autoHideHd.mouseout);
34165 this.el.un("mouseover", this.autoHideHd.mouseover);
34169 clearMonitor : function(){
34170 Roo.get(document).un("click", this.slideInIf, this);
34173 // these names are backwards but not changed for compat
34174 slideOut : function(){
34175 if(this.isSlid || this.el.hasActiveFx()){
34178 this.isSlid = true;
34179 if(this.collapseBtn){
34180 this.collapseBtn.hide();
34182 this.closeBtnState = this.closeBtn.getStyle('display');
34183 this.closeBtn.hide();
34185 this.stickBtn.show();
34188 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
34189 this.beforeSlide();
34190 this.el.setStyle("z-index", 10001);
34191 this.el.slideIn(this.getSlideAnchor(), {
34192 callback: function(){
34194 this.initAutoHide();
34195 Roo.get(document).on("click", this.slideInIf, this);
34196 this.fireEvent("slideshow", this);
34203 afterSlideIn : function(){
34204 this.clearAutoHide();
34205 this.isSlid = false;
34206 this.clearMonitor();
34207 this.el.setStyle("z-index", "");
34208 if(this.collapseBtn){
34209 this.collapseBtn.show();
34211 this.closeBtn.setStyle('display', this.closeBtnState);
34213 this.stickBtn.hide();
34215 this.fireEvent("slidehide", this);
34218 slideIn : function(cb){
34219 if(!this.isSlid || this.el.hasActiveFx()){
34223 this.isSlid = false;
34224 this.beforeSlide();
34225 this.el.slideOut(this.getSlideAnchor(), {
34226 callback: function(){
34227 this.el.setLeftTop(-10000, -10000);
34229 this.afterSlideIn();
34237 slideInIf : function(e){
34238 if(!e.within(this.el)){
34243 animateCollapse : function(){
34244 this.beforeSlide();
34245 this.el.setStyle("z-index", 20000);
34246 var anchor = this.getSlideAnchor();
34247 this.el.slideOut(anchor, {
34248 callback : function(){
34249 this.el.setStyle("z-index", "");
34250 this.collapsedEl.slideIn(anchor, {duration:.3});
34252 this.el.setLocation(-10000,-10000);
34254 this.fireEvent("collapsed", this);
34261 animateExpand : function(){
34262 this.beforeSlide();
34263 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
34264 this.el.setStyle("z-index", 20000);
34265 this.collapsedEl.hide({
34268 this.el.slideIn(this.getSlideAnchor(), {
34269 callback : function(){
34270 this.el.setStyle("z-index", "");
34273 this.split.el.show();
34275 this.fireEvent("invalidated", this);
34276 this.fireEvent("expanded", this);
34304 getAnchor : function(){
34305 return this.anchors[this.position];
34308 getCollapseAnchor : function(){
34309 return this.canchors[this.position];
34312 getSlideAnchor : function(){
34313 return this.sanchors[this.position];
34316 getAlignAdj : function(){
34317 var cm = this.cmargins;
34318 switch(this.position){
34334 getExpandAdj : function(){
34335 var c = this.collapsedEl, cm = this.cmargins;
34336 switch(this.position){
34338 return [-(cm.right+c.getWidth()+cm.left), 0];
34341 return [cm.right+c.getWidth()+cm.left, 0];
34344 return [0, -(cm.top+cm.bottom+c.getHeight())];
34347 return [0, cm.top+cm.bottom+c.getHeight()];
34353 * Ext JS Library 1.1.1
34354 * Copyright(c) 2006-2007, Ext JS, LLC.
34356 * Originally Released Under LGPL - original licence link has changed is not relivant.
34359 * <script type="text/javascript">
34362 * These classes are private internal classes
34364 Roo.bootstrap.layout.Center = function(config){
34365 config.region = "center";
34366 Roo.bootstrap.layout.Region.call(this, config);
34367 this.visible = true;
34368 this.minWidth = config.minWidth || 20;
34369 this.minHeight = config.minHeight || 20;
34372 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
34374 // center panel can't be hidden
34378 // center panel can't be hidden
34381 getMinWidth: function(){
34382 return this.minWidth;
34385 getMinHeight: function(){
34386 return this.minHeight;
34399 Roo.bootstrap.layout.North = function(config)
34401 config.region = 'north';
34402 config.cursor = 'n-resize';
34404 Roo.bootstrap.layout.Split.call(this, config);
34408 this.split.placement = Roo.bootstrap.SplitBar.TOP;
34409 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
34410 this.split.el.addClass("roo-layout-split-v");
34412 var size = config.initialSize || config.height;
34413 if(typeof size != "undefined"){
34414 this.el.setHeight(size);
34417 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
34419 orientation: Roo.bootstrap.SplitBar.VERTICAL,
34423 getBox : function(){
34424 if(this.collapsed){
34425 return this.collapsedEl.getBox();
34427 var box = this.el.getBox();
34429 box.height += this.split.el.getHeight();
34434 updateBox : function(box){
34435 if(this.split && !this.collapsed){
34436 box.height -= this.split.el.getHeight();
34437 this.split.el.setLeft(box.x);
34438 this.split.el.setTop(box.y+box.height);
34439 this.split.el.setWidth(box.width);
34441 if(this.collapsed){
34442 this.updateBody(box.width, null);
34444 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34452 Roo.bootstrap.layout.South = function(config){
34453 config.region = 'south';
34454 config.cursor = 's-resize';
34455 Roo.bootstrap.layout.Split.call(this, config);
34457 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
34458 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
34459 this.split.el.addClass("roo-layout-split-v");
34461 var size = config.initialSize || config.height;
34462 if(typeof size != "undefined"){
34463 this.el.setHeight(size);
34467 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
34468 orientation: Roo.bootstrap.SplitBar.VERTICAL,
34469 getBox : function(){
34470 if(this.collapsed){
34471 return this.collapsedEl.getBox();
34473 var box = this.el.getBox();
34475 var sh = this.split.el.getHeight();
34482 updateBox : function(box){
34483 if(this.split && !this.collapsed){
34484 var sh = this.split.el.getHeight();
34487 this.split.el.setLeft(box.x);
34488 this.split.el.setTop(box.y-sh);
34489 this.split.el.setWidth(box.width);
34491 if(this.collapsed){
34492 this.updateBody(box.width, null);
34494 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34498 Roo.bootstrap.layout.East = function(config){
34499 config.region = "east";
34500 config.cursor = "e-resize";
34501 Roo.bootstrap.layout.Split.call(this, config);
34503 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
34504 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
34505 this.split.el.addClass("roo-layout-split-h");
34507 var size = config.initialSize || config.width;
34508 if(typeof size != "undefined"){
34509 this.el.setWidth(size);
34512 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
34513 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
34514 getBox : function(){
34515 if(this.collapsed){
34516 return this.collapsedEl.getBox();
34518 var box = this.el.getBox();
34520 var sw = this.split.el.getWidth();
34527 updateBox : function(box){
34528 if(this.split && !this.collapsed){
34529 var sw = this.split.el.getWidth();
34531 this.split.el.setLeft(box.x);
34532 this.split.el.setTop(box.y);
34533 this.split.el.setHeight(box.height);
34536 if(this.collapsed){
34537 this.updateBody(null, box.height);
34539 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34543 Roo.bootstrap.layout.West = function(config){
34544 config.region = "west";
34545 config.cursor = "w-resize";
34547 Roo.bootstrap.layout.Split.call(this, config);
34549 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
34550 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
34551 this.split.el.addClass("roo-layout-split-h");
34555 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
34556 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
34558 onRender: function(ctr, pos)
34560 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
34561 var size = this.config.initialSize || this.config.width;
34562 if(typeof size != "undefined"){
34563 this.el.setWidth(size);
34567 getBox : function(){
34568 if(this.collapsed){
34569 return this.collapsedEl.getBox();
34571 var box = this.el.getBox();
34573 box.width += this.split.el.getWidth();
34578 updateBox : function(box){
34579 if(this.split && !this.collapsed){
34580 var sw = this.split.el.getWidth();
34582 this.split.el.setLeft(box.x+box.width);
34583 this.split.el.setTop(box.y);
34584 this.split.el.setHeight(box.height);
34586 if(this.collapsed){
34587 this.updateBody(null, box.height);
34589 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34592 Roo.namespace("Roo.bootstrap.panel");/*
34594 * Ext JS Library 1.1.1
34595 * Copyright(c) 2006-2007, Ext JS, LLC.
34597 * Originally Released Under LGPL - original licence link has changed is not relivant.
34600 * <script type="text/javascript">
34603 * @class Roo.ContentPanel
34604 * @extends Roo.util.Observable
34605 * A basic ContentPanel element.
34606 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
34607 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
34608 * @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
34609 * @cfg {Boolean} closable True if the panel can be closed/removed
34610 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
34611 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
34612 * @cfg {Toolbar} toolbar A toolbar for this panel
34613 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
34614 * @cfg {String} title The title for this panel
34615 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
34616 * @cfg {String} url Calls {@link #setUrl} with this value
34617 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
34618 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
34619 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
34620 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
34621 * @cfg {Boolean} badges render the badges
34624 * Create a new ContentPanel.
34625 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
34626 * @param {String/Object} config A string to set only the title or a config object
34627 * @param {String} content (optional) Set the HTML content for this panel
34628 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
34630 Roo.bootstrap.panel.Content = function( config){
34632 this.tpl = config.tpl || false;
34634 var el = config.el;
34635 var content = config.content;
34637 if(config.autoCreate){ // xtype is available if this is called from factory
34640 this.el = Roo.get(el);
34641 if(!this.el && config && config.autoCreate){
34642 if(typeof config.autoCreate == "object"){
34643 if(!config.autoCreate.id){
34644 config.autoCreate.id = config.id||el;
34646 this.el = Roo.DomHelper.append(document.body,
34647 config.autoCreate, true);
34649 var elcfg = { tag: "div",
34650 cls: "roo-layout-inactive-content",
34654 elcfg.html = config.html;
34658 this.el = Roo.DomHelper.append(document.body, elcfg , true);
34661 this.closable = false;
34662 this.loaded = false;
34663 this.active = false;
34666 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
34668 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
34670 this.wrapEl = this.el; //this.el.wrap();
34672 if (config.toolbar.items) {
34673 ti = config.toolbar.items ;
34674 delete config.toolbar.items ;
34678 this.toolbar.render(this.wrapEl, 'before');
34679 for(var i =0;i < ti.length;i++) {
34680 // Roo.log(['add child', items[i]]);
34681 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
34683 this.toolbar.items = nitems;
34684 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
34685 delete config.toolbar;
34689 // xtype created footer. - not sure if will work as we normally have to render first..
34690 if (this.footer && !this.footer.el && this.footer.xtype) {
34691 if (!this.wrapEl) {
34692 this.wrapEl = this.el.wrap();
34695 this.footer.container = this.wrapEl.createChild();
34697 this.footer = Roo.factory(this.footer, Roo);
34702 if(typeof config == "string"){
34703 this.title = config;
34705 Roo.apply(this, config);
34709 this.resizeEl = Roo.get(this.resizeEl, true);
34711 this.resizeEl = this.el;
34713 // handle view.xtype
34721 * Fires when this panel is activated.
34722 * @param {Roo.ContentPanel} this
34726 * @event deactivate
34727 * Fires when this panel is activated.
34728 * @param {Roo.ContentPanel} this
34730 "deactivate" : true,
34734 * Fires when this panel is resized if fitToFrame is true.
34735 * @param {Roo.ContentPanel} this
34736 * @param {Number} width The width after any component adjustments
34737 * @param {Number} height The height after any component adjustments
34743 * Fires when this tab is created
34744 * @param {Roo.ContentPanel} this
34755 if(this.autoScroll){
34756 this.resizeEl.setStyle("overflow", "auto");
34758 // fix randome scrolling
34759 //this.el.on('scroll', function() {
34760 // Roo.log('fix random scolling');
34761 // this.scrollTo('top',0);
34764 content = content || this.content;
34766 this.setContent(content);
34768 if(config && config.url){
34769 this.setUrl(this.url, this.params, this.loadOnce);
34774 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
34776 if (this.view && typeof(this.view.xtype) != 'undefined') {
34777 this.view.el = this.el.appendChild(document.createElement("div"));
34778 this.view = Roo.factory(this.view);
34779 this.view.render && this.view.render(false, '');
34783 this.fireEvent('render', this);
34786 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
34790 setRegion : function(region){
34791 this.region = region;
34792 this.setActiveClass(region && !this.background);
34796 setActiveClass: function(state)
34799 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
34800 this.el.setStyle('position','relative');
34802 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
34803 this.el.setStyle('position', 'absolute');
34808 * Returns the toolbar for this Panel if one was configured.
34809 * @return {Roo.Toolbar}
34811 getToolbar : function(){
34812 return this.toolbar;
34815 setActiveState : function(active)
34817 this.active = active;
34818 this.setActiveClass(active);
34820 this.fireEvent("deactivate", this);
34822 this.fireEvent("activate", this);
34826 * Updates this panel's element
34827 * @param {String} content The new content
34828 * @param {Boolean} loadScripts (optional) true to look for and process scripts
34830 setContent : function(content, loadScripts){
34831 this.el.update(content, loadScripts);
34834 ignoreResize : function(w, h){
34835 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
34838 this.lastSize = {width: w, height: h};
34843 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
34844 * @return {Roo.UpdateManager} The UpdateManager
34846 getUpdateManager : function(){
34847 return this.el.getUpdateManager();
34850 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
34851 * @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:
34854 url: "your-url.php",
34855 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
34856 callback: yourFunction,
34857 scope: yourObject, //(optional scope)
34860 text: "Loading...",
34865 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
34866 * 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.
34867 * @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}
34868 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
34869 * @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.
34870 * @return {Roo.ContentPanel} this
34873 var um = this.el.getUpdateManager();
34874 um.update.apply(um, arguments);
34880 * 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.
34881 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
34882 * @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)
34883 * @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)
34884 * @return {Roo.UpdateManager} The UpdateManager
34886 setUrl : function(url, params, loadOnce){
34887 if(this.refreshDelegate){
34888 this.removeListener("activate", this.refreshDelegate);
34890 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
34891 this.on("activate", this.refreshDelegate);
34892 return this.el.getUpdateManager();
34895 _handleRefresh : function(url, params, loadOnce){
34896 if(!loadOnce || !this.loaded){
34897 var updater = this.el.getUpdateManager();
34898 updater.update(url, params, this._setLoaded.createDelegate(this));
34902 _setLoaded : function(){
34903 this.loaded = true;
34907 * Returns this panel's id
34910 getId : function(){
34915 * Returns this panel's element - used by regiosn to add.
34916 * @return {Roo.Element}
34918 getEl : function(){
34919 return this.wrapEl || this.el;
34924 adjustForComponents : function(width, height)
34926 //Roo.log('adjustForComponents ');
34927 if(this.resizeEl != this.el){
34928 width -= this.el.getFrameWidth('lr');
34929 height -= this.el.getFrameWidth('tb');
34932 var te = this.toolbar.getEl();
34933 height -= te.getHeight();
34934 te.setWidth(width);
34937 var te = this.footer.getEl();
34938 Roo.log("footer:" + te.getHeight());
34940 height -= te.getHeight();
34941 te.setWidth(width);
34945 if(this.adjustments){
34946 width += this.adjustments[0];
34947 height += this.adjustments[1];
34949 return {"width": width, "height": height};
34952 setSize : function(width, height){
34953 if(this.fitToFrame && !this.ignoreResize(width, height)){
34954 if(this.fitContainer && this.resizeEl != this.el){
34955 this.el.setSize(width, height);
34957 var size = this.adjustForComponents(width, height);
34958 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
34959 this.fireEvent('resize', this, size.width, size.height);
34964 * Returns this panel's title
34967 getTitle : function(){
34972 * Set this panel's title
34973 * @param {String} title
34975 setTitle : function(title){
34976 this.title = title;
34978 this.region.updatePanelTitle(this, title);
34983 * Returns true is this panel was configured to be closable
34984 * @return {Boolean}
34986 isClosable : function(){
34987 return this.closable;
34990 beforeSlide : function(){
34992 this.resizeEl.clip();
34995 afterSlide : function(){
34997 this.resizeEl.unclip();
35001 * Force a content refresh from the URL specified in the {@link #setUrl} method.
35002 * Will fail silently if the {@link #setUrl} method has not been called.
35003 * This does not activate the panel, just updates its content.
35005 refresh : function(){
35006 if(this.refreshDelegate){
35007 this.loaded = false;
35008 this.refreshDelegate();
35013 * Destroys this panel
35015 destroy : function(){
35016 this.el.removeAllListeners();
35017 var tempEl = document.createElement("span");
35018 tempEl.appendChild(this.el.dom);
35019 tempEl.innerHTML = "";
35025 * form - if the content panel contains a form - this is a reference to it.
35026 * @type {Roo.form.Form}
35030 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
35031 * This contains a reference to it.
35037 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
35047 * @param {Object} cfg Xtype definition of item to add.
35051 getChildContainer: function () {
35052 return this.getEl();
35057 var ret = new Roo.factory(cfg);
35062 if (cfg.xtype.match(/^Form$/)) {
35065 //if (this.footer) {
35066 // el = this.footer.container.insertSibling(false, 'before');
35068 el = this.el.createChild();
35071 this.form = new Roo.form.Form(cfg);
35074 if ( this.form.allItems.length) {
35075 this.form.render(el.dom);
35079 // should only have one of theses..
35080 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
35081 // views.. should not be just added - used named prop 'view''
35083 cfg.el = this.el.appendChild(document.createElement("div"));
35086 var ret = new Roo.factory(cfg);
35088 ret.render && ret.render(false, ''); // render blank..
35098 * @class Roo.bootstrap.panel.Grid
35099 * @extends Roo.bootstrap.panel.Content
35101 * Create a new GridPanel.
35102 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
35103 * @param {Object} config A the config object
35109 Roo.bootstrap.panel.Grid = function(config)
35113 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
35114 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
35116 config.el = this.wrapper;
35117 //this.el = this.wrapper;
35119 if (config.container) {
35120 // ctor'ed from a Border/panel.grid
35123 this.wrapper.setStyle("overflow", "hidden");
35124 this.wrapper.addClass('roo-grid-container');
35129 if(config.toolbar){
35130 var tool_el = this.wrapper.createChild();
35131 this.toolbar = Roo.factory(config.toolbar);
35133 if (config.toolbar.items) {
35134 ti = config.toolbar.items ;
35135 delete config.toolbar.items ;
35139 this.toolbar.render(tool_el);
35140 for(var i =0;i < ti.length;i++) {
35141 // Roo.log(['add child', items[i]]);
35142 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
35144 this.toolbar.items = nitems;
35146 delete config.toolbar;
35149 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
35150 config.grid.scrollBody = true;;
35151 config.grid.monitorWindowResize = false; // turn off autosizing
35152 config.grid.autoHeight = false;
35153 config.grid.autoWidth = false;
35155 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
35157 if (config.background) {
35158 // render grid on panel activation (if panel background)
35159 this.on('activate', function(gp) {
35160 if (!gp.grid.rendered) {
35161 gp.grid.render(this.wrapper);
35162 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
35167 this.grid.render(this.wrapper);
35168 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
35171 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
35172 // ??? needed ??? config.el = this.wrapper;
35177 // xtype created footer. - not sure if will work as we normally have to render first..
35178 if (this.footer && !this.footer.el && this.footer.xtype) {
35180 var ctr = this.grid.getView().getFooterPanel(true);
35181 this.footer.dataSource = this.grid.dataSource;
35182 this.footer = Roo.factory(this.footer, Roo);
35183 this.footer.render(ctr);
35193 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
35194 getId : function(){
35195 return this.grid.id;
35199 * Returns the grid for this panel
35200 * @return {Roo.bootstrap.Table}
35202 getGrid : function(){
35206 setSize : function(width, height){
35207 if(!this.ignoreResize(width, height)){
35208 var grid = this.grid;
35209 var size = this.adjustForComponents(width, height);
35210 var gridel = grid.getGridEl();
35211 gridel.setSize(size.width, size.height);
35213 var thd = grid.getGridEl().select('thead',true).first();
35214 var tbd = grid.getGridEl().select('tbody', true).first();
35216 tbd.setSize(width, height - thd.getHeight());
35225 beforeSlide : function(){
35226 this.grid.getView().scroller.clip();
35229 afterSlide : function(){
35230 this.grid.getView().scroller.unclip();
35233 destroy : function(){
35234 this.grid.destroy();
35236 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
35241 * @class Roo.bootstrap.panel.Nest
35242 * @extends Roo.bootstrap.panel.Content
35244 * Create a new Panel, that can contain a layout.Border.
35247 * @param {Roo.BorderLayout} layout The layout for this panel
35248 * @param {String/Object} config A string to set only the title or a config object
35250 Roo.bootstrap.panel.Nest = function(config)
35252 // construct with only one argument..
35253 /* FIXME - implement nicer consturctors
35254 if (layout.layout) {
35256 layout = config.layout;
35257 delete config.layout;
35259 if (layout.xtype && !layout.getEl) {
35260 // then layout needs constructing..
35261 layout = Roo.factory(layout, Roo);
35265 config.el = config.layout.getEl();
35267 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
35269 config.layout.monitorWindowResize = false; // turn off autosizing
35270 this.layout = config.layout;
35271 this.layout.getEl().addClass("roo-layout-nested-layout");
35278 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
35280 setSize : function(width, height){
35281 if(!this.ignoreResize(width, height)){
35282 var size = this.adjustForComponents(width, height);
35283 var el = this.layout.getEl();
35284 if (size.height < 1) {
35285 el.setWidth(size.width);
35287 el.setSize(size.width, size.height);
35289 var touch = el.dom.offsetWidth;
35290 this.layout.layout();
35291 // ie requires a double layout on the first pass
35292 if(Roo.isIE && !this.initialized){
35293 this.initialized = true;
35294 this.layout.layout();
35299 // activate all subpanels if not currently active..
35301 setActiveState : function(active){
35302 this.active = active;
35303 this.setActiveClass(active);
35306 this.fireEvent("deactivate", this);
35310 this.fireEvent("activate", this);
35311 // not sure if this should happen before or after..
35312 if (!this.layout) {
35313 return; // should not happen..
35316 for (var r in this.layout.regions) {
35317 reg = this.layout.getRegion(r);
35318 if (reg.getActivePanel()) {
35319 //reg.showPanel(reg.getActivePanel()); // force it to activate..
35320 reg.setActivePanel(reg.getActivePanel());
35323 if (!reg.panels.length) {
35326 reg.showPanel(reg.getPanel(0));
35335 * Returns the nested BorderLayout for this panel
35336 * @return {Roo.BorderLayout}
35338 getLayout : function(){
35339 return this.layout;
35343 * Adds a xtype elements to the layout of the nested panel
35347 xtype : 'ContentPanel',
35354 xtype : 'NestedLayoutPanel',
35360 items : [ ... list of content panels or nested layout panels.. ]
35364 * @param {Object} cfg Xtype definition of item to add.
35366 addxtype : function(cfg) {
35367 return this.layout.addxtype(cfg);
35372 * Ext JS Library 1.1.1
35373 * Copyright(c) 2006-2007, Ext JS, LLC.
35375 * Originally Released Under LGPL - original licence link has changed is not relivant.
35378 * <script type="text/javascript">
35381 * @class Roo.TabPanel
35382 * @extends Roo.util.Observable
35383 * A lightweight tab container.
35387 // basic tabs 1, built from existing content
35388 var tabs = new Roo.TabPanel("tabs1");
35389 tabs.addTab("script", "View Script");
35390 tabs.addTab("markup", "View Markup");
35391 tabs.activate("script");
35393 // more advanced tabs, built from javascript
35394 var jtabs = new Roo.TabPanel("jtabs");
35395 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
35397 // set up the UpdateManager
35398 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
35399 var updater = tab2.getUpdateManager();
35400 updater.setDefaultUrl("ajax1.htm");
35401 tab2.on('activate', updater.refresh, updater, true);
35403 // Use setUrl for Ajax loading
35404 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
35405 tab3.setUrl("ajax2.htm", null, true);
35408 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
35411 jtabs.activate("jtabs-1");
35414 * Create a new TabPanel.
35415 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
35416 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
35418 Roo.bootstrap.panel.Tabs = function(config){
35420 * The container element for this TabPanel.
35421 * @type Roo.Element
35423 this.el = Roo.get(config.el);
35426 if(typeof config == "boolean"){
35427 this.tabPosition = config ? "bottom" : "top";
35429 Roo.apply(this, config);
35433 if(this.tabPosition == "bottom"){
35434 this.bodyEl = Roo.get(this.createBody(this.el.dom));
35435 this.el.addClass("roo-tabs-bottom");
35437 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
35438 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
35439 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
35441 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
35443 if(this.tabPosition != "bottom"){
35444 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
35445 * @type Roo.Element
35447 this.bodyEl = Roo.get(this.createBody(this.el.dom));
35448 this.el.addClass("roo-tabs-top");
35452 this.bodyEl.setStyle("position", "relative");
35454 this.active = null;
35455 this.activateDelegate = this.activate.createDelegate(this);
35460 * Fires when the active tab changes
35461 * @param {Roo.TabPanel} this
35462 * @param {Roo.TabPanelItem} activePanel The new active tab
35466 * @event beforetabchange
35467 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
35468 * @param {Roo.TabPanel} this
35469 * @param {Object} e Set cancel to true on this object to cancel the tab change
35470 * @param {Roo.TabPanelItem} tab The tab being changed to
35472 "beforetabchange" : true
35475 Roo.EventManager.onWindowResize(this.onResize, this);
35476 this.cpad = this.el.getPadding("lr");
35477 this.hiddenCount = 0;
35480 // toolbar on the tabbar support...
35481 if (this.toolbar) {
35482 alert("no toolbar support yet");
35483 this.toolbar = false;
35485 var tcfg = this.toolbar;
35486 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
35487 this.toolbar = new Roo.Toolbar(tcfg);
35488 if (Roo.isSafari) {
35489 var tbl = tcfg.container.child('table', true);
35490 tbl.setAttribute('width', '100%');
35498 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
35501 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
35503 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
35505 tabPosition : "top",
35507 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
35509 currentTabWidth : 0,
35511 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
35515 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
35519 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
35521 preferredTabWidth : 175,
35523 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
35525 resizeTabs : false,
35527 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
35529 monitorResize : true,
35531 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
35536 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
35537 * @param {String} id The id of the div to use <b>or create</b>
35538 * @param {String} text The text for the tab
35539 * @param {String} content (optional) Content to put in the TabPanelItem body
35540 * @param {Boolean} closable (optional) True to create a close icon on the tab
35541 * @return {Roo.TabPanelItem} The created TabPanelItem
35543 addTab : function(id, text, content, closable, tpl)
35545 var item = new Roo.bootstrap.panel.TabItem({
35549 closable : closable,
35552 this.addTabItem(item);
35554 item.setContent(content);
35560 * Returns the {@link Roo.TabPanelItem} with the specified id/index
35561 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
35562 * @return {Roo.TabPanelItem}
35564 getTab : function(id){
35565 return this.items[id];
35569 * Hides the {@link Roo.TabPanelItem} with the specified id/index
35570 * @param {String/Number} id The id or index of the TabPanelItem to hide.
35572 hideTab : function(id){
35573 var t = this.items[id];
35576 this.hiddenCount++;
35577 this.autoSizeTabs();
35582 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
35583 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
35585 unhideTab : function(id){
35586 var t = this.items[id];
35588 t.setHidden(false);
35589 this.hiddenCount--;
35590 this.autoSizeTabs();
35595 * Adds an existing {@link Roo.TabPanelItem}.
35596 * @param {Roo.TabPanelItem} item The TabPanelItem to add
35598 addTabItem : function(item){
35599 this.items[item.id] = item;
35600 this.items.push(item);
35601 // if(this.resizeTabs){
35602 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
35603 // this.autoSizeTabs();
35605 // item.autoSize();
35610 * Removes a {@link Roo.TabPanelItem}.
35611 * @param {String/Number} id The id or index of the TabPanelItem to remove.
35613 removeTab : function(id){
35614 var items = this.items;
35615 var tab = items[id];
35616 if(!tab) { return; }
35617 var index = items.indexOf(tab);
35618 if(this.active == tab && items.length > 1){
35619 var newTab = this.getNextAvailable(index);
35624 this.stripEl.dom.removeChild(tab.pnode.dom);
35625 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
35626 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
35628 items.splice(index, 1);
35629 delete this.items[tab.id];
35630 tab.fireEvent("close", tab);
35631 tab.purgeListeners();
35632 this.autoSizeTabs();
35635 getNextAvailable : function(start){
35636 var items = this.items;
35638 // look for a next tab that will slide over to
35639 // replace the one being removed
35640 while(index < items.length){
35641 var item = items[++index];
35642 if(item && !item.isHidden()){
35646 // if one isn't found select the previous tab (on the left)
35649 var item = items[--index];
35650 if(item && !item.isHidden()){
35658 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
35659 * @param {String/Number} id The id or index of the TabPanelItem to disable.
35661 disableTab : function(id){
35662 var tab = this.items[id];
35663 if(tab && this.active != tab){
35669 * Enables a {@link Roo.TabPanelItem} that is disabled.
35670 * @param {String/Number} id The id or index of the TabPanelItem to enable.
35672 enableTab : function(id){
35673 var tab = this.items[id];
35678 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
35679 * @param {String/Number} id The id or index of the TabPanelItem to activate.
35680 * @return {Roo.TabPanelItem} The TabPanelItem.
35682 activate : function(id){
35683 var tab = this.items[id];
35687 if(tab == this.active || tab.disabled){
35691 this.fireEvent("beforetabchange", this, e, tab);
35692 if(e.cancel !== true && !tab.disabled){
35694 this.active.hide();
35696 this.active = this.items[id];
35697 this.active.show();
35698 this.fireEvent("tabchange", this, this.active);
35704 * Gets the active {@link Roo.TabPanelItem}.
35705 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
35707 getActiveTab : function(){
35708 return this.active;
35712 * Updates the tab body element to fit the height of the container element
35713 * for overflow scrolling
35714 * @param {Number} targetHeight (optional) Override the starting height from the elements height
35716 syncHeight : function(targetHeight){
35717 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
35718 var bm = this.bodyEl.getMargins();
35719 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
35720 this.bodyEl.setHeight(newHeight);
35724 onResize : function(){
35725 if(this.monitorResize){
35726 this.autoSizeTabs();
35731 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
35733 beginUpdate : function(){
35734 this.updating = true;
35738 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
35740 endUpdate : function(){
35741 this.updating = false;
35742 this.autoSizeTabs();
35746 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
35748 autoSizeTabs : function(){
35749 var count = this.items.length;
35750 var vcount = count - this.hiddenCount;
35751 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
35754 var w = Math.max(this.el.getWidth() - this.cpad, 10);
35755 var availWidth = Math.floor(w / vcount);
35756 var b = this.stripBody;
35757 if(b.getWidth() > w){
35758 var tabs = this.items;
35759 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
35760 if(availWidth < this.minTabWidth){
35761 /*if(!this.sleft){ // incomplete scrolling code
35762 this.createScrollButtons();
35765 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
35768 if(this.currentTabWidth < this.preferredTabWidth){
35769 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
35775 * Returns the number of tabs in this TabPanel.
35778 getCount : function(){
35779 return this.items.length;
35783 * Resizes all the tabs to the passed width
35784 * @param {Number} The new width
35786 setTabWidth : function(width){
35787 this.currentTabWidth = width;
35788 for(var i = 0, len = this.items.length; i < len; i++) {
35789 if(!this.items[i].isHidden()) {
35790 this.items[i].setWidth(width);
35796 * Destroys this TabPanel
35797 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
35799 destroy : function(removeEl){
35800 Roo.EventManager.removeResizeListener(this.onResize, this);
35801 for(var i = 0, len = this.items.length; i < len; i++){
35802 this.items[i].purgeListeners();
35804 if(removeEl === true){
35805 this.el.update("");
35810 createStrip : function(container)
35812 var strip = document.createElement("nav");
35813 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
35814 container.appendChild(strip);
35818 createStripList : function(strip)
35820 // div wrapper for retard IE
35821 // returns the "tr" element.
35822 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
35823 //'<div class="x-tabs-strip-wrap">'+
35824 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
35825 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
35826 return strip.firstChild; //.firstChild.firstChild.firstChild;
35828 createBody : function(container)
35830 var body = document.createElement("div");
35831 Roo.id(body, "tab-body");
35832 //Roo.fly(body).addClass("x-tabs-body");
35833 Roo.fly(body).addClass("tab-content");
35834 container.appendChild(body);
35837 createItemBody :function(bodyEl, id){
35838 var body = Roo.getDom(id);
35840 body = document.createElement("div");
35843 //Roo.fly(body).addClass("x-tabs-item-body");
35844 Roo.fly(body).addClass("tab-pane");
35845 bodyEl.insertBefore(body, bodyEl.firstChild);
35849 createStripElements : function(stripEl, text, closable, tpl)
35851 var td = document.createElement("li"); // was td..
35854 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
35857 stripEl.appendChild(td);
35859 td.className = "x-tabs-closable";
35860 if(!this.closeTpl){
35861 this.closeTpl = new Roo.Template(
35862 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
35863 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
35864 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
35867 var el = this.closeTpl.overwrite(td, {"text": text});
35868 var close = el.getElementsByTagName("div")[0];
35869 var inner = el.getElementsByTagName("em")[0];
35870 return {"el": el, "close": close, "inner": inner};
35873 // not sure what this is..
35874 // if(!this.tabTpl){
35875 //this.tabTpl = new Roo.Template(
35876 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
35877 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
35879 // this.tabTpl = new Roo.Template(
35880 // '<a href="#">' +
35881 // '<span unselectable="on"' +
35882 // (this.disableTooltips ? '' : ' title="{text}"') +
35883 // ' >{text}</span></a>'
35889 var template = tpl || this.tabTpl || false;
35893 template = new Roo.Template(
35895 '<span unselectable="on"' +
35896 (this.disableTooltips ? '' : ' title="{text}"') +
35897 ' >{text}</span></a>'
35901 switch (typeof(template)) {
35905 template = new Roo.Template(template);
35911 var el = template.overwrite(td, {"text": text});
35913 var inner = el.getElementsByTagName("span")[0];
35915 return {"el": el, "inner": inner};
35923 * @class Roo.TabPanelItem
35924 * @extends Roo.util.Observable
35925 * Represents an individual item (tab plus body) in a TabPanel.
35926 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
35927 * @param {String} id The id of this TabPanelItem
35928 * @param {String} text The text for the tab of this TabPanelItem
35929 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
35931 Roo.bootstrap.panel.TabItem = function(config){
35933 * The {@link Roo.TabPanel} this TabPanelItem belongs to
35934 * @type Roo.TabPanel
35936 this.tabPanel = config.panel;
35938 * The id for this TabPanelItem
35941 this.id = config.id;
35943 this.disabled = false;
35945 this.text = config.text;
35947 this.loaded = false;
35948 this.closable = config.closable;
35951 * The body element for this TabPanelItem.
35952 * @type Roo.Element
35954 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
35955 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
35956 this.bodyEl.setStyle("display", "block");
35957 this.bodyEl.setStyle("zoom", "1");
35958 //this.hideAction();
35960 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
35962 this.el = Roo.get(els.el);
35963 this.inner = Roo.get(els.inner, true);
35964 this.textEl = Roo.get(this.el.dom.firstChild, true);
35965 this.pnode = Roo.get(els.el.parentNode, true);
35966 this.el.on("mousedown", this.onTabMouseDown, this);
35967 this.el.on("click", this.onTabClick, this);
35969 if(config.closable){
35970 var c = Roo.get(els.close, true);
35971 c.dom.title = this.closeText;
35972 c.addClassOnOver("close-over");
35973 c.on("click", this.closeClick, this);
35979 * Fires when this tab becomes the active tab.
35980 * @param {Roo.TabPanel} tabPanel The parent TabPanel
35981 * @param {Roo.TabPanelItem} this
35985 * @event beforeclose
35986 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
35987 * @param {Roo.TabPanelItem} this
35988 * @param {Object} e Set cancel to true on this object to cancel the close.
35990 "beforeclose": true,
35993 * Fires when this tab is closed.
35994 * @param {Roo.TabPanelItem} this
35998 * @event deactivate
35999 * Fires when this tab is no longer the active tab.
36000 * @param {Roo.TabPanel} tabPanel The parent TabPanel
36001 * @param {Roo.TabPanelItem} this
36003 "deactivate" : true
36005 this.hidden = false;
36007 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
36010 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
36012 purgeListeners : function(){
36013 Roo.util.Observable.prototype.purgeListeners.call(this);
36014 this.el.removeAllListeners();
36017 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
36020 this.pnode.addClass("active");
36023 this.tabPanel.stripWrap.repaint();
36025 this.fireEvent("activate", this.tabPanel, this);
36029 * Returns true if this tab is the active tab.
36030 * @return {Boolean}
36032 isActive : function(){
36033 return this.tabPanel.getActiveTab() == this;
36037 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
36040 this.pnode.removeClass("active");
36042 this.fireEvent("deactivate", this.tabPanel, this);
36045 hideAction : function(){
36046 this.bodyEl.hide();
36047 this.bodyEl.setStyle("position", "absolute");
36048 this.bodyEl.setLeft("-20000px");
36049 this.bodyEl.setTop("-20000px");
36052 showAction : function(){
36053 this.bodyEl.setStyle("position", "relative");
36054 this.bodyEl.setTop("");
36055 this.bodyEl.setLeft("");
36056 this.bodyEl.show();
36060 * Set the tooltip for the tab.
36061 * @param {String} tooltip The tab's tooltip
36063 setTooltip : function(text){
36064 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
36065 this.textEl.dom.qtip = text;
36066 this.textEl.dom.removeAttribute('title');
36068 this.textEl.dom.title = text;
36072 onTabClick : function(e){
36073 e.preventDefault();
36074 this.tabPanel.activate(this.id);
36077 onTabMouseDown : function(e){
36078 e.preventDefault();
36079 this.tabPanel.activate(this.id);
36082 getWidth : function(){
36083 return this.inner.getWidth();
36086 setWidth : function(width){
36087 var iwidth = width - this.pnode.getPadding("lr");
36088 this.inner.setWidth(iwidth);
36089 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
36090 this.pnode.setWidth(width);
36094 * Show or hide the tab
36095 * @param {Boolean} hidden True to hide or false to show.
36097 setHidden : function(hidden){
36098 this.hidden = hidden;
36099 this.pnode.setStyle("display", hidden ? "none" : "");
36103 * Returns true if this tab is "hidden"
36104 * @return {Boolean}
36106 isHidden : function(){
36107 return this.hidden;
36111 * Returns the text for this tab
36114 getText : function(){
36118 autoSize : function(){
36119 //this.el.beginMeasure();
36120 this.textEl.setWidth(1);
36122 * #2804 [new] Tabs in Roojs
36123 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
36125 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
36126 //this.el.endMeasure();
36130 * Sets the text for the tab (Note: this also sets the tooltip text)
36131 * @param {String} text The tab's text and tooltip
36133 setText : function(text){
36135 this.textEl.update(text);
36136 this.setTooltip(text);
36137 //if(!this.tabPanel.resizeTabs){
36138 // this.autoSize();
36142 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
36144 activate : function(){
36145 this.tabPanel.activate(this.id);
36149 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
36151 disable : function(){
36152 if(this.tabPanel.active != this){
36153 this.disabled = true;
36154 this.pnode.addClass("disabled");
36159 * Enables this TabPanelItem if it was previously disabled.
36161 enable : function(){
36162 this.disabled = false;
36163 this.pnode.removeClass("disabled");
36167 * Sets the content for this TabPanelItem.
36168 * @param {String} content The content
36169 * @param {Boolean} loadScripts true to look for and load scripts
36171 setContent : function(content, loadScripts){
36172 this.bodyEl.update(content, loadScripts);
36176 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
36177 * @return {Roo.UpdateManager} The UpdateManager
36179 getUpdateManager : function(){
36180 return this.bodyEl.getUpdateManager();
36184 * Set a URL to be used to load the content for this TabPanelItem.
36185 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
36186 * @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)
36187 * @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)
36188 * @return {Roo.UpdateManager} The UpdateManager
36190 setUrl : function(url, params, loadOnce){
36191 if(this.refreshDelegate){
36192 this.un('activate', this.refreshDelegate);
36194 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
36195 this.on("activate", this.refreshDelegate);
36196 return this.bodyEl.getUpdateManager();
36200 _handleRefresh : function(url, params, loadOnce){
36201 if(!loadOnce || !this.loaded){
36202 var updater = this.bodyEl.getUpdateManager();
36203 updater.update(url, params, this._setLoaded.createDelegate(this));
36208 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
36209 * Will fail silently if the setUrl method has not been called.
36210 * This does not activate the panel, just updates its content.
36212 refresh : function(){
36213 if(this.refreshDelegate){
36214 this.loaded = false;
36215 this.refreshDelegate();
36220 _setLoaded : function(){
36221 this.loaded = true;
36225 closeClick : function(e){
36228 this.fireEvent("beforeclose", this, o);
36229 if(o.cancel !== true){
36230 this.tabPanel.removeTab(this.id);
36234 * The text displayed in the tooltip for the close icon.
36237 closeText : "Close this tab"