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');
383 * @class Roo.bootstrap.Body
384 * @extends Roo.bootstrap.Component
385 * Bootstrap Body class
389 * @param {Object} config The config object
392 Roo.bootstrap.Body = function(config){
394 config = config || {};
396 Roo.bootstrap.Body.superclass.constructor.call(this, config);
397 this.el = Roo.get(config.el ? config.el : document.body );
398 if (this.cls && this.cls.length) {
399 Roo.get(document.body).addClass(this.cls);
403 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
405 is_body : true,// just to make sure it's constructed?
410 onRender : function(ct, position)
412 /* Roo.log("Roo.bootstrap.Body - onRender");
413 if (this.cls && this.cls.length) {
414 Roo.get(document.body).addClass(this.cls);
433 * @class Roo.bootstrap.ButtonGroup
434 * @extends Roo.bootstrap.Component
435 * Bootstrap ButtonGroup class
436 * @cfg {String} size lg | sm | xs (default empty normal)
437 * @cfg {String} align vertical | justified (default none)
438 * @cfg {String} direction up | down (default down)
439 * @cfg {Boolean} toolbar false | true
440 * @cfg {Boolean} btn true | false
445 * @param {Object} config The config object
448 Roo.bootstrap.ButtonGroup = function(config){
449 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
452 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
460 getAutoCreate : function(){
466 cfg.html = this.html || cfg.html;
477 if (['vertical','justified'].indexOf(this.align)!==-1) {
478 cfg.cls = 'btn-group-' + this.align;
480 if (this.align == 'justified') {
481 console.log(this.items);
485 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
486 cfg.cls += ' btn-group-' + this.size;
489 if (this.direction == 'up') {
490 cfg.cls += ' dropup' ;
506 * @class Roo.bootstrap.Button
507 * @extends Roo.bootstrap.Component
508 * Bootstrap Button class
509 * @cfg {String} html The button content
510 * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default
511 * @cfg {String} size ( lg | sm | xs)
512 * @cfg {String} tag ( a | input | submit)
513 * @cfg {String} href empty or href
514 * @cfg {Boolean} disabled default false;
515 * @cfg {Boolean} isClose default false;
516 * @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)
517 * @cfg {String} badge text for badge
518 * @cfg {String} theme default
519 * @cfg {Boolean} inverse
520 * @cfg {Boolean} toggle
521 * @cfg {String} ontext text for on toggle state
522 * @cfg {String} offtext text for off toggle state
523 * @cfg {Boolean} defaulton
524 * @cfg {Boolean} preventDefault default true
525 * @cfg {Boolean} removeClass remove the standard class..
526 * @cfg {String} target target for a href. (_self|_blank|_parent|_top| other)
529 * Create a new button
530 * @param {Object} config The config object
534 Roo.bootstrap.Button = function(config){
535 Roo.bootstrap.Button.superclass.constructor.call(this, config);
536 this.weightClass = ["btn-default",
548 * When a butotn is pressed
549 * @param {Roo.bootstrap.Button} this
550 * @param {Roo.EventObject} e
555 * After the button has been toggles
556 * @param {Roo.EventObject} e
557 * @param {boolean} pressed (also available as button.pressed)
563 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
581 preventDefault: true,
590 getAutoCreate : function(){
598 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
599 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
604 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
606 if (this.toggle == true) {
609 cls: 'slider-frame roo-button',
614 'data-off-text':'OFF',
615 cls: 'slider-button',
621 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
622 cfg.cls += ' '+this.weight;
631 cfg["aria-hidden"] = true;
633 cfg.html = "×";
639 if (this.theme==='default') {
640 cfg.cls = 'btn roo-button';
642 //if (this.parentType != 'Navbar') {
643 this.weight = this.weight.length ? this.weight : 'default';
645 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
647 cfg.cls += ' btn-' + this.weight;
649 } else if (this.theme==='glow') {
652 cfg.cls = 'btn-glow roo-button';
654 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
656 cfg.cls += ' ' + this.weight;
662 this.cls += ' inverse';
667 cfg.cls += ' active';
671 cfg.disabled = 'disabled';
675 Roo.log('changing to ul' );
677 this.glyphicon = 'caret';
680 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
682 //gsRoo.log(this.parentType);
683 if (this.parentType === 'Navbar' && !this.parent().bar) {
684 Roo.log('changing to li?');
693 href : this.href || '#'
696 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
697 cfg.cls += ' dropdown';
704 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
706 if (this.glyphicon) {
707 cfg.html = ' ' + cfg.html;
712 cls: 'glyphicon glyphicon-' + this.glyphicon
722 // cfg.cls='btn roo-button';
726 var value = cfg.html;
731 cls: 'glyphicon glyphicon-' + this.glyphicon,
750 cfg.cls += ' dropdown';
751 cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
754 if (cfg.tag !== 'a' && this.href !== '') {
755 throw "Tag must be a to set href.";
756 } else if (this.href.length > 0) {
757 cfg.href = this.href;
760 if(this.removeClass){
765 cfg.target = this.target;
770 initEvents: function() {
771 // Roo.log('init events?');
772 // Roo.log(this.el.dom);
775 if (typeof (this.menu) != 'undefined') {
776 this.menu.parentType = this.xtype;
777 this.menu.triggerEl = this.el;
778 this.addxtype(Roo.apply({}, this.menu));
782 if (this.el.hasClass('roo-button')) {
783 this.el.on('click', this.onClick, this);
785 this.el.select('.roo-button').on('click', this.onClick, this);
788 if(this.removeClass){
789 this.el.on('click', this.onClick, this);
792 this.el.enableDisplayMode();
795 onClick : function(e)
802 Roo.log('button on click ');
803 if(this.preventDefault){
806 if (this.pressed === true || this.pressed === false) {
807 this.pressed = !this.pressed;
808 this.el[this.pressed ? 'addClass' : 'removeClass']('active');
809 this.fireEvent('toggle', this, e, this.pressed);
813 this.fireEvent('click', this, e);
817 * Enables this button
821 this.disabled = false;
822 this.el.removeClass('disabled');
826 * Disable this button
830 this.disabled = true;
831 this.el.addClass('disabled');
834 * sets the active state on/off,
835 * @param {Boolean} state (optional) Force a particular state
837 setActive : function(v) {
839 this.el[v ? 'addClass' : 'removeClass']('active');
842 * toggles the current active state
844 toggleActive : function()
846 var active = this.el.hasClass('active');
847 this.setActive(!active);
851 setText : function(str)
853 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
857 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
868 setWeight : function(str)
870 this.el.removeClass(this.weightClass);
871 this.el.addClass('btn-' + str);
885 * @class Roo.bootstrap.Column
886 * @extends Roo.bootstrap.Component
887 * Bootstrap Column class
888 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
889 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
890 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
891 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
892 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
893 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
894 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
895 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
898 * @cfg {Boolean} hidden (true|false) hide the element
899 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
900 * @cfg {String} fa (ban|check|...) font awesome icon
901 * @cfg {Number} fasize (1|2|....) font awsome size
903 * @cfg {String} icon (info-sign|check|...) glyphicon name
905 * @cfg {String} html content of column.
908 * Create a new Column
909 * @param {Object} config The config object
912 Roo.bootstrap.Column = function(config){
913 Roo.bootstrap.Column.superclass.constructor.call(this, config);
916 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
934 getAutoCreate : function(){
935 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
943 ['xs','sm','md','lg'].map(function(size){
944 //Roo.log( size + ':' + settings[size]);
946 if (settings[size+'off'] !== false) {
947 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
950 if (settings[size] === false) {
954 if (!settings[size]) { // 0 = hidden
955 cfg.cls += ' hidden-' + size;
958 cfg.cls += ' col-' + size + '-' + settings[size];
963 cfg.cls += ' hidden';
966 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
967 cfg.cls +=' alert alert-' + this.alert;
971 if (this.html.length) {
972 cfg.html = this.html;
976 if (this.fasize > 1) {
977 fasize = ' fa-' + this.fasize + 'x';
979 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
984 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1003 * @class Roo.bootstrap.Container
1004 * @extends Roo.bootstrap.Component
1005 * Bootstrap Container class
1006 * @cfg {Boolean} jumbotron is it a jumbotron element
1007 * @cfg {String} html content of element
1008 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1009 * @cfg {String} panel (primary|success|info|warning|danger) render as panel - type - primary/success.....
1010 * @cfg {String} header content of header (for panel)
1011 * @cfg {String} footer content of footer (for panel)
1012 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1013 * @cfg {String} tag (header|aside|section) type of HTML tag.
1014 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1015 * @cfg {String} fa font awesome icon
1016 * @cfg {String} icon (info-sign|check|...) glyphicon name
1017 * @cfg {Boolean} hidden (true|false) hide the element
1018 * @cfg {Boolean} expandable (true|false) default false
1019 * @cfg {Boolean} expanded (true|false) default true
1020 * @cfg {String} rheader contet on the right of header
1021 * @cfg {Boolean} clickable (true|false) default false
1025 * Create a new Container
1026 * @param {Object} config The config object
1029 Roo.bootstrap.Container = function(config){
1030 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1036 * After the panel has been expand
1038 * @param {Roo.bootstrap.Container} this
1043 * After the panel has been collapsed
1045 * @param {Roo.bootstrap.Container} this
1050 * When a element is chick
1051 * @param {Roo.bootstrap.Container} this
1052 * @param {Roo.EventObject} e
1058 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1076 getChildContainer : function() {
1082 if (this.panel.length) {
1083 return this.el.select('.panel-body',true).first();
1090 getAutoCreate : function(){
1093 tag : this.tag || 'div',
1097 if (this.jumbotron) {
1098 cfg.cls = 'jumbotron';
1103 // - this is applied by the parent..
1105 // cfg.cls = this.cls + '';
1108 if (this.sticky.length) {
1110 var bd = Roo.get(document.body);
1111 if (!bd.hasClass('bootstrap-sticky')) {
1112 bd.addClass('bootstrap-sticky');
1113 Roo.select('html',true).setStyle('height', '100%');
1116 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1120 if (this.well.length) {
1121 switch (this.well) {
1124 cfg.cls +=' well well-' +this.well;
1133 cfg.cls += ' hidden';
1137 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1138 cfg.cls +=' alert alert-' + this.alert;
1143 if (this.panel.length) {
1144 cfg.cls += ' panel panel-' + this.panel;
1146 if (this.header.length) {
1150 if(this.expandable){
1152 cfg.cls = cfg.cls + ' expandable';
1156 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1164 cls : 'panel-title',
1165 html : (this.expandable ? ' ' : '') + this.header
1169 cls: 'panel-header-right',
1175 cls : 'panel-heading',
1176 style : this.expandable ? 'cursor: pointer' : '',
1184 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1189 if (this.footer.length) {
1191 cls : 'panel-footer',
1200 body.html = this.html || cfg.html;
1201 // prefix with the icons..
1203 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1206 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1211 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1212 cfg.cls = 'container';
1218 initEvents: function()
1220 if(this.expandable){
1221 var headerEl = this.headerEl();
1224 headerEl.on('click', this.onToggleClick, this);
1229 this.el.on('click', this.onClick, this);
1234 onToggleClick : function()
1236 var headerEl = this.headerEl();
1252 if(this.fireEvent('expand', this)) {
1254 this.expanded = true;
1256 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1258 this.el.select('.panel-body',true).first().removeClass('hide');
1260 var toggleEl = this.toggleEl();
1266 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1271 collapse : function()
1273 if(this.fireEvent('collapse', this)) {
1275 this.expanded = false;
1277 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1278 this.el.select('.panel-body',true).first().addClass('hide');
1280 var toggleEl = this.toggleEl();
1286 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1290 toggleEl : function()
1292 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1296 return this.el.select('.panel-heading .fa',true).first();
1299 headerEl : function()
1301 if(!this.el || !this.panel.length || !this.header.length){
1305 return this.el.select('.panel-heading',true).first()
1310 if(!this.el || !this.panel.length){
1314 return this.el.select('.panel-body',true).first()
1317 titleEl : function()
1319 if(!this.el || !this.panel.length || !this.header.length){
1323 return this.el.select('.panel-title',true).first();
1326 setTitle : function(v)
1328 var titleEl = this.titleEl();
1334 titleEl.dom.innerHTML = v;
1337 getTitle : function()
1340 var titleEl = this.titleEl();
1346 return titleEl.dom.innerHTML;
1349 setRightTitle : function(v)
1351 var t = this.el.select('.panel-header-right',true).first();
1357 t.dom.innerHTML = v;
1360 onClick : function(e)
1364 this.fireEvent('click', this, e);
1378 * @class Roo.bootstrap.Img
1379 * @extends Roo.bootstrap.Component
1380 * Bootstrap Img class
1381 * @cfg {Boolean} imgResponsive false | true
1382 * @cfg {String} border rounded | circle | thumbnail
1383 * @cfg {String} src image source
1384 * @cfg {String} alt image alternative text
1385 * @cfg {String} href a tag href
1386 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1387 * @cfg {String} xsUrl xs image source
1388 * @cfg {String} smUrl sm image source
1389 * @cfg {String} mdUrl md image source
1390 * @cfg {String} lgUrl lg image source
1393 * Create a new Input
1394 * @param {Object} config The config object
1397 Roo.bootstrap.Img = function(config){
1398 Roo.bootstrap.Img.superclass.constructor.call(this, config);
1404 * The img click event for the img.
1405 * @param {Roo.EventObject} e
1411 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1413 imgResponsive: true,
1423 getAutoCreate : function()
1425 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1426 return this.createSingleImg();
1431 cls: 'roo-image-responsive-group',
1436 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1438 if(!_this[size + 'Url']){
1444 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1445 html: _this.html || cfg.html,
1446 src: _this[size + 'Url']
1449 img.cls += ' roo-image-responsive-' + size;
1451 var s = ['xs', 'sm', 'md', 'lg'];
1453 s.splice(s.indexOf(size), 1);
1455 Roo.each(s, function(ss){
1456 img.cls += ' hidden-' + ss;
1459 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1460 cfg.cls += ' img-' + _this.border;
1464 cfg.alt = _this.alt;
1477 a.target = _this.target;
1481 cfg.cn.push((_this.href) ? a : img);
1488 createSingleImg : function()
1492 cls: (this.imgResponsive) ? 'img-responsive' : '',
1494 src : 'about:blank' // just incase src get's set to undefined?!?
1497 cfg.html = this.html || cfg.html;
1499 cfg.src = this.src || cfg.src;
1501 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1502 cfg.cls += ' img-' + this.border;
1519 a.target = this.target;
1524 return (this.href) ? a : cfg;
1527 initEvents: function()
1530 this.el.on('click', this.onClick, this);
1535 onClick : function(e)
1537 Roo.log('img onclick');
1538 this.fireEvent('click', this, e);
1541 * Sets the url of the image - used to update it
1542 * @param {String} url the url of the image
1545 setSrc : function(url)
1549 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1550 this.el.dom.src = url;
1554 this.el.select('img', true).first().dom.src = url;
1570 * @class Roo.bootstrap.Link
1571 * @extends Roo.bootstrap.Component
1572 * Bootstrap Link Class
1573 * @cfg {String} alt image alternative text
1574 * @cfg {String} href a tag href
1575 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1576 * @cfg {String} html the content of the link.
1577 * @cfg {String} anchor name for the anchor link
1578 * @cfg {String} fa - favicon
1580 * @cfg {Boolean} preventDefault (true | false) default false
1584 * Create a new Input
1585 * @param {Object} config The config object
1588 Roo.bootstrap.Link = function(config){
1589 Roo.bootstrap.Link.superclass.constructor.call(this, config);
1595 * The img click event for the img.
1596 * @param {Roo.EventObject} e
1602 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
1606 preventDefault: false,
1612 getAutoCreate : function()
1614 var html = this.html || '';
1616 if (this.fa !== false) {
1617 html = '<i class="fa fa-' + this.fa + '"></i>';
1622 // anchor's do not require html/href...
1623 if (this.anchor === false) {
1625 cfg.href = this.href || '#';
1627 cfg.name = this.anchor;
1628 if (this.html !== false || this.fa !== false) {
1631 if (this.href !== false) {
1632 cfg.href = this.href;
1636 if(this.alt !== false){
1641 if(this.target !== false) {
1642 cfg.target = this.target;
1648 initEvents: function() {
1650 if(!this.href || this.preventDefault){
1651 this.el.on('click', this.onClick, this);
1655 onClick : function(e)
1657 if(this.preventDefault){
1660 //Roo.log('img onclick');
1661 this.fireEvent('click', this, e);
1674 * @class Roo.bootstrap.Header
1675 * @extends Roo.bootstrap.Component
1676 * Bootstrap Header class
1677 * @cfg {String} html content of header
1678 * @cfg {Number} level (1|2|3|4|5|6) default 1
1681 * Create a new Header
1682 * @param {Object} config The config object
1686 Roo.bootstrap.Header = function(config){
1687 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1690 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1698 getAutoCreate : function(){
1703 tag: 'h' + (1 *this.level),
1704 html: this.html || ''
1716 * Ext JS Library 1.1.1
1717 * Copyright(c) 2006-2007, Ext JS, LLC.
1719 * Originally Released Under LGPL - original licence link has changed is not relivant.
1722 * <script type="text/javascript">
1726 * @class Roo.bootstrap.MenuMgr
1727 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1730 Roo.bootstrap.MenuMgr = function(){
1731 var menus, active, groups = {}, attached = false, lastShow = new Date();
1733 // private - called when first menu is created
1736 active = new Roo.util.MixedCollection();
1737 Roo.get(document).addKeyListener(27, function(){
1738 if(active.length > 0){
1746 if(active && active.length > 0){
1747 var c = active.clone();
1757 if(active.length < 1){
1758 Roo.get(document).un("mouseup", onMouseDown);
1766 var last = active.last();
1767 lastShow = new Date();
1770 Roo.get(document).on("mouseup", onMouseDown);
1775 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1776 m.parentMenu.activeChild = m;
1777 }else if(last && last.isVisible()){
1778 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1783 function onBeforeHide(m){
1785 m.activeChild.hide();
1787 if(m.autoHideTimer){
1788 clearTimeout(m.autoHideTimer);
1789 delete m.autoHideTimer;
1794 function onBeforeShow(m){
1795 var pm = m.parentMenu;
1796 if(!pm && !m.allowOtherMenus){
1798 }else if(pm && pm.activeChild && active != m){
1799 pm.activeChild.hide();
1803 // private this should really trigger on mouseup..
1804 function onMouseDown(e){
1805 Roo.log("on Mouse Up");
1807 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1808 Roo.log("MenuManager hideAll");
1817 function onBeforeCheck(mi, state){
1819 var g = groups[mi.group];
1820 for(var i = 0, l = g.length; i < l; i++){
1822 g[i].setChecked(false);
1831 * Hides all menus that are currently visible
1833 hideAll : function(){
1838 register : function(menu){
1842 menus[menu.id] = menu;
1843 menu.on("beforehide", onBeforeHide);
1844 menu.on("hide", onHide);
1845 menu.on("beforeshow", onBeforeShow);
1846 menu.on("show", onShow);
1848 if(g && menu.events["checkchange"]){
1852 groups[g].push(menu);
1853 menu.on("checkchange", onCheck);
1858 * Returns a {@link Roo.menu.Menu} object
1859 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1860 * be used to generate and return a new Menu instance.
1862 get : function(menu){
1863 if(typeof menu == "string"){ // menu id
1865 }else if(menu.events){ // menu instance
1868 /*else if(typeof menu.length == 'number'){ // array of menu items?
1869 return new Roo.bootstrap.Menu({items:menu});
1870 }else{ // otherwise, must be a config
1871 return new Roo.bootstrap.Menu(menu);
1878 unregister : function(menu){
1879 delete menus[menu.id];
1880 menu.un("beforehide", onBeforeHide);
1881 menu.un("hide", onHide);
1882 menu.un("beforeshow", onBeforeShow);
1883 menu.un("show", onShow);
1885 if(g && menu.events["checkchange"]){
1886 groups[g].remove(menu);
1887 menu.un("checkchange", onCheck);
1892 registerCheckable : function(menuItem){
1893 var g = menuItem.group;
1898 groups[g].push(menuItem);
1899 menuItem.on("beforecheckchange", onBeforeCheck);
1904 unregisterCheckable : function(menuItem){
1905 var g = menuItem.group;
1907 groups[g].remove(menuItem);
1908 menuItem.un("beforecheckchange", onBeforeCheck);
1920 * @class Roo.bootstrap.Menu
1921 * @extends Roo.bootstrap.Component
1922 * Bootstrap Menu class - container for MenuItems
1923 * @cfg {String} type (dropdown|treeview|submenu) type of menu
1924 * @cfg {bool} hidden if the menu should be hidden when rendered.
1925 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
1926 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
1930 * @param {Object} config The config object
1934 Roo.bootstrap.Menu = function(config){
1935 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1936 if (this.registerMenu && this.type != 'treeview') {
1937 Roo.bootstrap.MenuMgr.register(this);
1942 * Fires before this menu is displayed
1943 * @param {Roo.menu.Menu} this
1948 * Fires before this menu is hidden
1949 * @param {Roo.menu.Menu} this
1954 * Fires after this menu is displayed
1955 * @param {Roo.menu.Menu} this
1960 * Fires after this menu is hidden
1961 * @param {Roo.menu.Menu} this
1966 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1967 * @param {Roo.menu.Menu} this
1968 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1969 * @param {Roo.EventObject} e
1974 * Fires when the mouse is hovering over this menu
1975 * @param {Roo.menu.Menu} this
1976 * @param {Roo.EventObject} e
1977 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1982 * Fires when the mouse exits this menu
1983 * @param {Roo.menu.Menu} this
1984 * @param {Roo.EventObject} e
1985 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1990 * Fires when a menu item contained in this menu is clicked
1991 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1992 * @param {Roo.EventObject} e
1996 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1999 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
2003 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
2006 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2008 registerMenu : true,
2010 menuItems :false, // stores the menu items..
2020 getChildContainer : function() {
2024 getAutoCreate : function(){
2026 //if (['right'].indexOf(this.align)!==-1) {
2027 // cfg.cn[1].cls += ' pull-right'
2033 cls : 'dropdown-menu' ,
2034 style : 'z-index:1000'
2038 if (this.type === 'submenu') {
2039 cfg.cls = 'submenu active';
2041 if (this.type === 'treeview') {
2042 cfg.cls = 'treeview-menu';
2047 initEvents : function() {
2049 // Roo.log("ADD event");
2050 // Roo.log(this.triggerEl.dom);
2052 this.triggerEl.on('click', this.onTriggerClick, this);
2054 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2056 this.triggerEl.addClass('dropdown-toggle');
2059 this.el.on('touchstart' , this.onTouch, this);
2061 this.el.on('click' , this.onClick, this);
2063 this.el.on("mouseover", this.onMouseOver, this);
2064 this.el.on("mouseout", this.onMouseOut, this);
2068 findTargetItem : function(e)
2070 var t = e.getTarget(".dropdown-menu-item", this.el, true);
2074 //Roo.log(t); Roo.log(t.id);
2076 //Roo.log(this.menuitems);
2077 return this.menuitems.get(t.id);
2079 //return this.items.get(t.menuItemId);
2085 onTouch : function(e)
2087 Roo.log("menu.onTouch");
2088 //e.stopEvent(); this make the user popdown broken
2092 onClick : function(e)
2094 Roo.log("menu.onClick");
2096 var t = this.findTargetItem(e);
2097 if(!t || t.isContainer){
2102 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
2103 if(t == this.activeItem && t.shouldDeactivate(e)){
2104 this.activeItem.deactivate();
2105 delete this.activeItem;
2109 this.setActiveItem(t, true);
2117 Roo.log('pass click event');
2121 this.fireEvent("click", this, t, e);
2125 if(!t.href.length || t.href == '#'){
2126 (function() { _this.hide(); }).defer(100);
2131 onMouseOver : function(e){
2132 var t = this.findTargetItem(e);
2135 // if(t.canActivate && !t.disabled){
2136 // this.setActiveItem(t, true);
2140 this.fireEvent("mouseover", this, e, t);
2142 isVisible : function(){
2143 return !this.hidden;
2145 onMouseOut : function(e){
2146 var t = this.findTargetItem(e);
2149 // if(t == this.activeItem && t.shouldDeactivate(e)){
2150 // this.activeItem.deactivate();
2151 // delete this.activeItem;
2154 this.fireEvent("mouseout", this, e, t);
2159 * Displays this menu relative to another element
2160 * @param {String/HTMLElement/Roo.Element} element The element to align to
2161 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2162 * the element (defaults to this.defaultAlign)
2163 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2165 show : function(el, pos, parentMenu){
2166 this.parentMenu = parentMenu;
2170 this.fireEvent("beforeshow", this);
2171 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2174 * Displays this menu at a specific xy position
2175 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2176 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2178 showAt : function(xy, parentMenu, /* private: */_e){
2179 this.parentMenu = parentMenu;
2184 this.fireEvent("beforeshow", this);
2185 //xy = this.el.adjustForConstraints(xy);
2189 this.hideMenuItems();
2190 this.hidden = false;
2191 this.triggerEl.addClass('open');
2193 if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2194 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2197 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2202 this.fireEvent("show", this);
2208 this.doFocus.defer(50, this);
2212 doFocus : function(){
2214 this.focusEl.focus();
2219 * Hides this menu and optionally all parent menus
2220 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2222 hide : function(deep)
2225 this.hideMenuItems();
2226 if(this.el && this.isVisible()){
2227 this.fireEvent("beforehide", this);
2228 if(this.activeItem){
2229 this.activeItem.deactivate();
2230 this.activeItem = null;
2232 this.triggerEl.removeClass('open');;
2234 this.fireEvent("hide", this);
2236 if(deep === true && this.parentMenu){
2237 this.parentMenu.hide(true);
2241 onTriggerClick : function(e)
2243 Roo.log('trigger click');
2245 var target = e.getTarget();
2247 Roo.log(target.nodeName.toLowerCase());
2249 if(target.nodeName.toLowerCase() === 'i'){
2255 onTriggerPress : function(e)
2257 Roo.log('trigger press');
2258 //Roo.log(e.getTarget());
2259 // Roo.log(this.triggerEl.dom);
2261 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2262 var pel = Roo.get(e.getTarget());
2263 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2264 Roo.log('is treeview or dropdown?');
2268 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2272 if (this.isVisible()) {
2277 this.show(this.triggerEl, false, false);
2280 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2287 hideMenuItems : function()
2289 Roo.log("hide Menu Items");
2293 //$(backdrop).remove()
2294 this.el.select('.open',true).each(function(aa) {
2296 aa.removeClass('open');
2297 //var parent = getParent($(this))
2298 //var relatedTarget = { relatedTarget: this }
2300 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2301 //if (e.isDefaultPrevented()) return
2302 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2305 addxtypeChild : function (tree, cntr) {
2306 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2308 this.menuitems.add(comp);
2329 * @class Roo.bootstrap.MenuItem
2330 * @extends Roo.bootstrap.Component
2331 * Bootstrap MenuItem class
2332 * @cfg {String} html the menu label
2333 * @cfg {String} href the link
2334 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2335 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2336 * @cfg {Boolean} active used on sidebars to highlight active itesm
2337 * @cfg {String} fa favicon to show on left of menu item.
2338 * @cfg {Roo.bootsrap.Menu} menu the child menu.
2342 * Create a new MenuItem
2343 * @param {Object} config The config object
2347 Roo.bootstrap.MenuItem = function(config){
2348 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2353 * The raw click event for the entire grid.
2354 * @param {Roo.bootstrap.MenuItem} this
2355 * @param {Roo.EventObject} e
2361 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2365 preventDefault: false,
2366 isContainer : false,
2370 getAutoCreate : function(){
2372 if(this.isContainer){
2375 cls: 'dropdown-menu-item'
2389 if (this.fa !== false) {
2392 cls : 'fa fa-' + this.fa
2401 cls: 'dropdown-menu-item',
2404 if (this.parent().type == 'treeview') {
2405 cfg.cls = 'treeview-menu';
2408 cfg.cls += ' active';
2413 anc.href = this.href || cfg.cn[0].href ;
2414 ctag.html = this.html || cfg.cn[0].html ;
2418 initEvents: function()
2420 if (this.parent().type == 'treeview') {
2421 this.el.select('a').on('click', this.onClick, this);
2425 this.menu.parentType = this.xtype;
2426 this.menu.triggerEl = this.el;
2427 this.menu = this.addxtype(Roo.apply({}, this.menu));
2431 onClick : function(e)
2433 Roo.log('item on click ');
2435 if(this.preventDefault){
2438 //this.parent().hideMenuItems();
2440 this.fireEvent('click', this, e);
2459 * @class Roo.bootstrap.MenuSeparator
2460 * @extends Roo.bootstrap.Component
2461 * Bootstrap MenuSeparator class
2464 * Create a new MenuItem
2465 * @param {Object} config The config object
2469 Roo.bootstrap.MenuSeparator = function(config){
2470 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2473 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2475 getAutoCreate : function(){
2494 * @class Roo.bootstrap.Modal
2495 * @extends Roo.bootstrap.Component
2496 * Bootstrap Modal class
2497 * @cfg {String} title Title of dialog
2498 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2499 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2500 * @cfg {Boolean} specificTitle default false
2501 * @cfg {Array} buttons Array of buttons or standard button set..
2502 * @cfg {String} buttonPosition (left|right|center) default right
2503 * @cfg {Boolean} animate default true
2504 * @cfg {Boolean} allow_close default true
2505 * @cfg {Boolean} fitwindow default false
2506 * @cfg {String} size (sm|lg) default empty
2510 * Create a new Modal Dialog
2511 * @param {Object} config The config object
2514 Roo.bootstrap.Modal = function(config){
2515 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2520 * The raw btnclick event for the button
2521 * @param {Roo.EventObject} e
2526 * Fire when dialog resize
2527 * @param {Roo.bootstrap.Modal} this
2528 * @param {Roo.EventObject} e
2532 this.buttons = this.buttons || [];
2535 this.tmpl = Roo.factory(this.tmpl);
2540 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2542 title : 'test dialog',
2552 specificTitle: false,
2554 buttonPosition: 'right',
2573 onRender : function(ct, position)
2575 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2578 var cfg = Roo.apply({}, this.getAutoCreate());
2581 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2583 //if (!cfg.name.length) {
2587 cfg.cls += ' ' + this.cls;
2590 cfg.style = this.style;
2592 this.el = Roo.get(document.body).createChild(cfg, position);
2594 //var type = this.el.dom.type;
2597 if(this.tabIndex !== undefined){
2598 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2601 this.dialogEl = this.el.select('.modal-dialog',true).first();
2602 this.bodyEl = this.el.select('.modal-body',true).first();
2603 this.closeEl = this.el.select('.modal-header .close', true).first();
2604 this.headerEl = this.el.select('.modal-header',true).first();
2605 this.titleEl = this.el.select('.modal-title',true).first();
2606 this.footerEl = this.el.select('.modal-footer',true).first();
2608 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2609 this.maskEl.enableDisplayMode("block");
2611 //this.el.addClass("x-dlg-modal");
2613 if (this.buttons.length) {
2614 Roo.each(this.buttons, function(bb) {
2615 var b = Roo.apply({}, bb);
2616 b.xns = b.xns || Roo.bootstrap;
2617 b.xtype = b.xtype || 'Button';
2618 if (typeof(b.listeners) == 'undefined') {
2619 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2622 var btn = Roo.factory(b);
2624 btn.render(this.el.select('.modal-footer div').first());
2628 // render the children.
2631 if(typeof(this.items) != 'undefined'){
2632 var items = this.items;
2635 for(var i =0;i < items.length;i++) {
2636 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2640 this.items = nitems;
2642 // where are these used - they used to be body/close/footer
2646 //this.el.addClass([this.fieldClass, this.cls]);
2650 getAutoCreate : function(){
2655 html : this.html || ''
2660 cls : 'modal-title',
2664 if(this.specificTitle){
2670 if (this.allow_close) {
2682 if(this.size.length){
2683 size = 'modal-' + this.size;
2688 style : 'display: none',
2691 cls: "modal-dialog " + size,
2694 cls : "modal-content",
2697 cls : 'modal-header',
2702 cls : 'modal-footer',
2706 cls: 'btn-' + this.buttonPosition
2723 modal.cls += ' fade';
2729 getChildContainer : function() {
2734 getButtonContainer : function() {
2735 return this.el.select('.modal-footer div',true).first();
2738 initEvents : function()
2740 if (this.allow_close) {
2741 this.closeEl.on('click', this.hide, this);
2743 Roo.EventManager.onWindowResize(this.resize, this, true);
2750 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2751 if (this.fitwindow) {
2752 var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2753 var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2758 setSize : function(w,h)
2768 if (!this.rendered) {
2772 this.el.setStyle('display', 'block');
2774 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
2777 this.el.addClass('in');
2780 this.el.addClass('in');
2784 // not sure how we can show data in here..
2786 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2789 Roo.get(document.body).addClass("x-body-masked");
2791 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2792 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2797 this.fireEvent('show', this);
2799 // set zindex here - otherwise it appears to be ignored...
2800 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2803 this.items.forEach( function(e) {
2804 e.layout ? e.layout() : false;
2812 if(this.fireEvent("beforehide", this) !== false){
2814 Roo.get(document.body).removeClass("x-body-masked");
2815 this.el.removeClass('in');
2816 this.el.select('.modal-dialog', true).first().setStyle('transform','');
2818 if(this.animate){ // why
2820 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2822 this.el.setStyle('display', 'none');
2824 this.fireEvent('hide', this);
2828 addButton : function(str, cb)
2832 var b = Roo.apply({}, { html : str } );
2833 b.xns = b.xns || Roo.bootstrap;
2834 b.xtype = b.xtype || 'Button';
2835 if (typeof(b.listeners) == 'undefined') {
2836 b.listeners = { click : cb.createDelegate(this) };
2839 var btn = Roo.factory(b);
2841 btn.render(this.el.select('.modal-footer div').first());
2847 setDefaultButton : function(btn)
2849 //this.el.select('.modal-footer').()
2853 resizeTo: function(w,h)
2857 this.dialogEl.setWidth(w);
2858 if (this.diff === false) {
2859 this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2862 this.bodyEl.setHeight(h-this.diff);
2864 this.fireEvent('resize', this);
2867 setContentSize : function(w, h)
2871 onButtonClick: function(btn,e)
2874 this.fireEvent('btnclick', btn.name, e);
2877 * Set the title of the Dialog
2878 * @param {String} str new Title
2880 setTitle: function(str) {
2881 this.titleEl.dom.innerHTML = str;
2884 * Set the body of the Dialog
2885 * @param {String} str new Title
2887 setBody: function(str) {
2888 this.bodyEl.dom.innerHTML = str;
2891 * Set the body of the Dialog using the template
2892 * @param {Obj} data - apply this data to the template and replace the body contents.
2894 applyBody: function(obj)
2897 Roo.log("Error - using apply Body without a template");
2900 this.tmpl.overwrite(this.bodyEl, obj);
2906 Roo.apply(Roo.bootstrap.Modal, {
2908 * Button config that displays a single OK button
2917 * Button config that displays Yes and No buttons
2933 * Button config that displays OK and Cancel buttons
2948 * Button config that displays Yes, No and Cancel buttons
2972 * messagebox - can be used as a replace
2976 * @class Roo.MessageBox
2977 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
2981 Roo.Msg.alert('Status', 'Changes saved successfully.');
2983 // Prompt for user data:
2984 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2986 // process text value...
2990 // Show a dialog using config options:
2992 title:'Save Changes?',
2993 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2994 buttons: Roo.Msg.YESNOCANCEL,
3001 Roo.bootstrap.MessageBox = function(){
3002 var dlg, opt, mask, waitTimer;
3003 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3004 var buttons, activeTextEl, bwidth;
3008 var handleButton = function(button){
3010 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3014 var handleHide = function(){
3016 dlg.el.removeClass(opt.cls);
3019 // Roo.TaskMgr.stop(waitTimer);
3020 // waitTimer = null;
3025 var updateButtons = function(b){
3028 buttons["ok"].hide();
3029 buttons["cancel"].hide();
3030 buttons["yes"].hide();
3031 buttons["no"].hide();
3032 //dlg.footer.dom.style.display = 'none';
3035 dlg.footerEl.dom.style.display = '';
3036 for(var k in buttons){
3037 if(typeof buttons[k] != "function"){
3040 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3041 width += buttons[k].el.getWidth()+15;
3051 var handleEsc = function(d, k, e){
3052 if(opt && opt.closable !== false){
3062 * Returns a reference to the underlying {@link Roo.BasicDialog} element
3063 * @return {Roo.BasicDialog} The BasicDialog element
3065 getDialog : function(){
3067 dlg = new Roo.bootstrap.Modal( {
3070 //constraintoviewport:false,
3072 //collapsible : false,
3077 //buttonAlign:"center",
3078 closeClick : function(){
3079 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3082 handleButton("cancel");
3087 dlg.on("hide", handleHide);
3089 //dlg.addKeyListener(27, handleEsc);
3091 this.buttons = buttons;
3092 var bt = this.buttonText;
3093 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3094 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3095 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3096 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3098 bodyEl = dlg.bodyEl.createChild({
3100 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3101 '<textarea class="roo-mb-textarea"></textarea>' +
3102 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
3104 msgEl = bodyEl.dom.firstChild;
3105 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3106 textboxEl.enableDisplayMode();
3107 textboxEl.addKeyListener([10,13], function(){
3108 if(dlg.isVisible() && opt && opt.buttons){
3111 }else if(opt.buttons.yes){
3112 handleButton("yes");
3116 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3117 textareaEl.enableDisplayMode();
3118 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3119 progressEl.enableDisplayMode();
3121 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3122 //var pf = progressEl.dom.firstChild;
3124 //pp = Roo.get(pf.firstChild);
3125 //pp.setHeight(pf.offsetHeight);
3133 * Updates the message box body text
3134 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3135 * the XHTML-compliant non-breaking space character '&#160;')
3136 * @return {Roo.MessageBox} This message box
3138 updateText : function(text)
3140 if(!dlg.isVisible() && !opt.width){
3141 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3142 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3144 msgEl.innerHTML = text || ' ';
3146 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3147 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3149 Math.min(opt.width || cw , this.maxWidth),
3150 Math.max(opt.minWidth || this.minWidth, bwidth)
3153 activeTextEl.setWidth(w);
3155 if(dlg.isVisible()){
3156 dlg.fixedcenter = false;
3158 // to big, make it scroll. = But as usual stupid IE does not support
3161 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3162 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3163 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3165 bodyEl.dom.style.height = '';
3166 bodyEl.dom.style.overflowY = '';
3169 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3171 bodyEl.dom.style.overflowX = '';
3174 dlg.setContentSize(w, bodyEl.getHeight());
3175 if(dlg.isVisible()){
3176 dlg.fixedcenter = true;
3182 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
3183 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3184 * @param {Number} value Any number between 0 and 1 (e.g., .5)
3185 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3186 * @return {Roo.MessageBox} This message box
3188 updateProgress : function(value, text){
3190 this.updateText(text);
3192 if (pp) { // weird bug on my firefox - for some reason this is not defined
3193 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3199 * Returns true if the message box is currently displayed
3200 * @return {Boolean} True if the message box is visible, else false
3202 isVisible : function(){
3203 return dlg && dlg.isVisible();
3207 * Hides the message box if it is displayed
3210 if(this.isVisible()){
3216 * Displays a new message box, or reinitializes an existing message box, based on the config options
3217 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3218 * The following config object properties are supported:
3220 Property Type Description
3221 ---------- --------------- ------------------------------------------------------------------------------------
3222 animEl String/Element An id or Element from which the message box should animate as it opens and
3223 closes (defaults to undefined)
3224 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3225 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3226 closable Boolean False to hide the top-right close button (defaults to true). Note that
3227 progress and wait dialogs will ignore this property and always hide the
3228 close button as they can only be closed programmatically.
3229 cls String A custom CSS class to apply to the message box element
3230 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3231 displayed (defaults to 75)
3232 fn Function A callback function to execute after closing the dialog. The arguments to the
3233 function will be btn (the name of the button that was clicked, if applicable,
3234 e.g. "ok"), and text (the value of the active text field, if applicable).
3235 Progress and wait dialogs will ignore this option since they do not respond to
3236 user actions and can only be closed programmatically, so any required function
3237 should be called by the same code after it closes the dialog.
3238 icon String A CSS class that provides a background image to be used as an icon for
3239 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3240 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3241 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3242 modal Boolean False to allow user interaction with the page while the message box is
3243 displayed (defaults to true)
3244 msg String A string that will replace the existing message box body text (defaults
3245 to the XHTML-compliant non-breaking space character ' ')
3246 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3247 progress Boolean True to display a progress bar (defaults to false)
3248 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3249 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3250 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3251 title String The title text
3252 value String The string value to set into the active textbox element if displayed
3253 wait Boolean True to display a progress bar (defaults to false)
3254 width Number The width of the dialog in pixels
3261 msg: 'Please enter your address:',
3263 buttons: Roo.MessageBox.OKCANCEL,
3266 animEl: 'addAddressBtn'
3269 * @param {Object} config Configuration options
3270 * @return {Roo.MessageBox} This message box
3272 show : function(options)
3275 // this causes nightmares if you show one dialog after another
3276 // especially on callbacks..
3278 if(this.isVisible()){
3281 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3282 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3283 Roo.log("New Dialog Message:" + options.msg )
3284 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3285 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3288 var d = this.getDialog();
3290 d.setTitle(opt.title || " ");
3291 d.closeEl.setDisplayed(opt.closable !== false);
3292 activeTextEl = textboxEl;
3293 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3298 textareaEl.setHeight(typeof opt.multiline == "number" ?
3299 opt.multiline : this.defaultTextHeight);
3300 activeTextEl = textareaEl;
3309 progressEl.setDisplayed(opt.progress === true);
3310 this.updateProgress(0);
3311 activeTextEl.dom.value = opt.value || "";
3313 dlg.setDefaultButton(activeTextEl);
3315 var bs = opt.buttons;
3319 }else if(bs && bs.yes){
3320 db = buttons["yes"];
3322 dlg.setDefaultButton(db);
3324 bwidth = updateButtons(opt.buttons);
3325 this.updateText(opt.msg);
3327 d.el.addClass(opt.cls);
3329 d.proxyDrag = opt.proxyDrag === true;
3330 d.modal = opt.modal !== false;
3331 d.mask = opt.modal !== false ? mask : false;
3333 // force it to the end of the z-index stack so it gets a cursor in FF
3334 document.body.appendChild(dlg.el.dom);
3335 d.animateTarget = null;
3336 d.show(options.animEl);
3342 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3343 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3344 * and closing the message box when the process is complete.
3345 * @param {String} title The title bar text
3346 * @param {String} msg The message box body text
3347 * @return {Roo.MessageBox} This message box
3349 progress : function(title, msg){
3356 minWidth: this.minProgressWidth,
3363 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3364 * If a callback function is passed it will be called after the user clicks the button, and the
3365 * id of the button that was clicked will be passed as the only parameter to the callback
3366 * (could also be the top-right close button).
3367 * @param {String} title The title bar text
3368 * @param {String} msg The message box body text
3369 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3370 * @param {Object} scope (optional) The scope of the callback function
3371 * @return {Roo.MessageBox} This message box
3373 alert : function(title, msg, fn, scope)
3388 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3389 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3390 * You are responsible for closing the message box when the process is complete.
3391 * @param {String} msg The message box body text
3392 * @param {String} title (optional) The title bar text
3393 * @return {Roo.MessageBox} This message box
3395 wait : function(msg, title){
3406 waitTimer = Roo.TaskMgr.start({
3408 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3416 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3417 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3418 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3419 * @param {String} title The title bar text
3420 * @param {String} msg The message box body text
3421 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3422 * @param {Object} scope (optional) The scope of the callback function
3423 * @return {Roo.MessageBox} This message box
3425 confirm : function(title, msg, fn, scope){
3429 buttons: this.YESNO,
3438 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3439 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3440 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3441 * (could also be the top-right close button) and the text that was entered will be passed as the two
3442 * parameters to the callback.
3443 * @param {String} title The title bar text
3444 * @param {String} msg The message box body text
3445 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3446 * @param {Object} scope (optional) The scope of the callback function
3447 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3448 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3449 * @return {Roo.MessageBox} This message box
3451 prompt : function(title, msg, fn, scope, multiline){
3455 buttons: this.OKCANCEL,
3460 multiline: multiline,
3467 * Button config that displays a single OK button
3472 * Button config that displays Yes and No buttons
3475 YESNO : {yes:true, no:true},
3477 * Button config that displays OK and Cancel buttons
3480 OKCANCEL : {ok:true, cancel:true},
3482 * Button config that displays Yes, No and Cancel buttons
3485 YESNOCANCEL : {yes:true, no:true, cancel:true},
3488 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3491 defaultTextHeight : 75,
3493 * The maximum width in pixels of the message box (defaults to 600)
3498 * The minimum width in pixels of the message box (defaults to 100)
3503 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3504 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3507 minProgressWidth : 250,
3509 * An object containing the default button text strings that can be overriden for localized language support.
3510 * Supported properties are: ok, cancel, yes and no.
3511 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3524 * Shorthand for {@link Roo.MessageBox}
3526 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3527 Roo.Msg = Roo.Msg || Roo.MessageBox;
3536 * @class Roo.bootstrap.Navbar
3537 * @extends Roo.bootstrap.Component
3538 * Bootstrap Navbar class
3541 * Create a new Navbar
3542 * @param {Object} config The config object
3546 Roo.bootstrap.Navbar = function(config){
3547 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3551 * @event beforetoggle
3552 * Fire before toggle the menu
3553 * @param {Roo.EventObject} e
3555 "beforetoggle" : true
3559 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3568 getAutoCreate : function(){
3571 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3575 initEvents :function ()
3577 //Roo.log(this.el.select('.navbar-toggle',true));
3578 this.el.select('.navbar-toggle',true).on('click', function() {
3579 if(this.fireEvent('beforetoggle', this) !== false){
3580 this.el.select('.navbar-collapse',true).toggleClass('in');
3590 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3592 var size = this.el.getSize();
3593 this.maskEl.setSize(size.width, size.height);
3594 this.maskEl.enableDisplayMode("block");
3603 getChildContainer : function()
3605 if (this.el.select('.collapse').getCount()) {
3606 return this.el.select('.collapse',true).first();
3639 * @class Roo.bootstrap.NavSimplebar
3640 * @extends Roo.bootstrap.Navbar
3641 * Bootstrap Sidebar class
3643 * @cfg {Boolean} inverse is inverted color
3645 * @cfg {String} type (nav | pills | tabs)
3646 * @cfg {Boolean} arrangement stacked | justified
3647 * @cfg {String} align (left | right) alignment
3649 * @cfg {Boolean} main (true|false) main nav bar? default false
3650 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3652 * @cfg {String} tag (header|footer|nav|div) default is nav
3658 * Create a new Sidebar
3659 * @param {Object} config The config object
3663 Roo.bootstrap.NavSimplebar = function(config){
3664 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3667 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3683 getAutoCreate : function(){
3687 tag : this.tag || 'div',
3700 this.type = this.type || 'nav';
3701 if (['tabs','pills'].indexOf(this.type)!==-1) {
3702 cfg.cn[0].cls += ' nav-' + this.type
3706 if (this.type!=='nav') {
3707 Roo.log('nav type must be nav/tabs/pills')
3709 cfg.cn[0].cls += ' navbar-nav'
3715 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3716 cfg.cn[0].cls += ' nav-' + this.arrangement;
3720 if (this.align === 'right') {
3721 cfg.cn[0].cls += ' navbar-right';
3725 cfg.cls += ' navbar-inverse';
3752 * @class Roo.bootstrap.NavHeaderbar
3753 * @extends Roo.bootstrap.NavSimplebar
3754 * Bootstrap Sidebar class
3756 * @cfg {String} brand what is brand
3757 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3758 * @cfg {String} brand_href href of the brand
3759 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
3760 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3761 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3762 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3765 * Create a new Sidebar
3766 * @param {Object} config The config object
3770 Roo.bootstrap.NavHeaderbar = function(config){
3771 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3775 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3782 desktopCenter : false,
3785 getAutoCreate : function(){
3788 tag: this.nav || 'nav',
3795 if (this.desktopCenter) {
3796 cn.push({cls : 'container', cn : []});
3803 cls: 'navbar-header',
3808 cls: 'navbar-toggle',
3809 'data-toggle': 'collapse',
3814 html: 'Toggle navigation'
3836 cls: 'collapse navbar-collapse',
3840 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3842 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3843 cfg.cls += ' navbar-' + this.position;
3845 // tag can override this..
3847 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
3850 if (this.brand !== '') {
3853 href: this.brand_href ? this.brand_href : '#',
3854 cls: 'navbar-brand',
3862 cfg.cls += ' main-nav';
3870 getHeaderChildContainer : function()
3872 if (this.srButton && this.el.select('.navbar-header').getCount()) {
3873 return this.el.select('.navbar-header',true).first();
3876 return this.getChildContainer();
3880 initEvents : function()
3882 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3884 if (this.autohide) {
3889 Roo.get(document).on('scroll',function(e) {
3890 var ns = Roo.get(document).getScroll().top;
3891 var os = prevScroll;
3895 ft.removeClass('slideDown');
3896 ft.addClass('slideUp');
3899 ft.removeClass('slideUp');
3900 ft.addClass('slideDown');
3921 * @class Roo.bootstrap.NavSidebar
3922 * @extends Roo.bootstrap.Navbar
3923 * Bootstrap Sidebar class
3926 * Create a new Sidebar
3927 * @param {Object} config The config object
3931 Roo.bootstrap.NavSidebar = function(config){
3932 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3935 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
3937 sidebar : true, // used by Navbar Item and NavbarGroup at present...
3939 getAutoCreate : function(){
3944 cls: 'sidebar sidebar-nav'
3966 * @class Roo.bootstrap.NavGroup
3967 * @extends Roo.bootstrap.Component
3968 * Bootstrap NavGroup class
3969 * @cfg {String} align (left|right)
3970 * @cfg {Boolean} inverse
3971 * @cfg {String} type (nav|pills|tab) default nav
3972 * @cfg {String} navId - reference Id for navbar.
3976 * Create a new nav group
3977 * @param {Object} config The config object
3980 Roo.bootstrap.NavGroup = function(config){
3981 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3984 Roo.bootstrap.NavGroup.register(this);
3988 * Fires when the active item changes
3989 * @param {Roo.bootstrap.NavGroup} this
3990 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3991 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
3998 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
4009 getAutoCreate : function()
4011 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4018 if (['tabs','pills'].indexOf(this.type)!==-1) {
4019 cfg.cls += ' nav-' + this.type
4021 if (this.type!=='nav') {
4022 Roo.log('nav type must be nav/tabs/pills')
4024 cfg.cls += ' navbar-nav'
4027 if (this.parent().sidebar) {
4030 cls: 'dashboard-menu sidebar-menu'
4036 if (this.form === true) {
4042 if (this.align === 'right') {
4043 cfg.cls += ' navbar-right';
4045 cfg.cls += ' navbar-left';
4049 if (this.align === 'right') {
4050 cfg.cls += ' navbar-right';
4054 cfg.cls += ' navbar-inverse';
4062 * sets the active Navigation item
4063 * @param {Roo.bootstrap.NavItem} the new current navitem
4065 setActiveItem : function(item)
4068 Roo.each(this.navItems, function(v){
4073 v.setActive(false, true);
4080 item.setActive(true, true);
4081 this.fireEvent('changed', this, item, prev);
4086 * gets the active Navigation item
4087 * @return {Roo.bootstrap.NavItem} the current navitem
4089 getActive : function()
4093 Roo.each(this.navItems, function(v){
4104 indexOfNav : function()
4108 Roo.each(this.navItems, function(v,i){
4119 * adds a Navigation item
4120 * @param {Roo.bootstrap.NavItem} the navitem to add
4122 addItem : function(cfg)
4124 var cn = new Roo.bootstrap.NavItem(cfg);
4126 cn.parentId = this.id;
4127 cn.onRender(this.el, null);
4131 * register a Navigation item
4132 * @param {Roo.bootstrap.NavItem} the navitem to add
4134 register : function(item)
4136 this.navItems.push( item);
4137 item.navId = this.navId;
4142 * clear all the Navigation item
4145 clearAll : function()
4148 this.el.dom.innerHTML = '';
4151 getNavItem: function(tabId)
4154 Roo.each(this.navItems, function(e) {
4155 if (e.tabId == tabId) {
4165 setActiveNext : function()
4167 var i = this.indexOfNav(this.getActive());
4168 if (i > this.navItems.length) {
4171 this.setActiveItem(this.navItems[i+1]);
4173 setActivePrev : function()
4175 var i = this.indexOfNav(this.getActive());
4179 this.setActiveItem(this.navItems[i-1]);
4181 clearWasActive : function(except) {
4182 Roo.each(this.navItems, function(e) {
4183 if (e.tabId != except.tabId && e.was_active) {
4184 e.was_active = false;
4191 getWasActive : function ()
4194 Roo.each(this.navItems, function(e) {
4209 Roo.apply(Roo.bootstrap.NavGroup, {
4213 * register a Navigation Group
4214 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4216 register : function(navgrp)
4218 this.groups[navgrp.navId] = navgrp;
4222 * fetch a Navigation Group based on the navigation ID
4223 * @param {string} the navgroup to add
4224 * @returns {Roo.bootstrap.NavGroup} the navgroup
4226 get: function(navId) {
4227 if (typeof(this.groups[navId]) == 'undefined') {
4229 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4231 return this.groups[navId] ;
4246 * @class Roo.bootstrap.NavItem
4247 * @extends Roo.bootstrap.Component
4248 * Bootstrap Navbar.NavItem class
4249 * @cfg {String} href link to
4250 * @cfg {String} html content of button
4251 * @cfg {String} badge text inside badge
4252 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4253 * @cfg {String} glyphicon name of glyphicon
4254 * @cfg {String} icon name of font awesome icon
4255 * @cfg {Boolean} active Is item active
4256 * @cfg {Boolean} disabled Is item disabled
4258 * @cfg {Boolean} preventDefault (true | false) default false
4259 * @cfg {String} tabId the tab that this item activates.
4260 * @cfg {String} tagtype (a|span) render as a href or span?
4261 * @cfg {Boolean} animateRef (true|false) link to element default false
4264 * Create a new Navbar Item
4265 * @param {Object} config The config object
4267 Roo.bootstrap.NavItem = function(config){
4268 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4273 * The raw click event for the entire grid.
4274 * @param {Roo.EventObject} e
4279 * Fires when the active item active state changes
4280 * @param {Roo.bootstrap.NavItem} this
4281 * @param {boolean} state the new state
4287 * Fires when scroll to element
4288 * @param {Roo.bootstrap.NavItem} this
4289 * @param {Object} options
4290 * @param {Roo.EventObject} e
4298 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4306 preventDefault : false,
4313 getAutoCreate : function(){
4322 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4324 if (this.disabled) {
4325 cfg.cls += ' disabled';
4328 if (this.href || this.html || this.glyphicon || this.icon) {
4332 href : this.href || "#",
4333 html: this.html || ''
4338 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4341 if(this.glyphicon) {
4342 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4347 cfg.cn[0].html += " <span class='caret'></span>";
4351 if (this.badge !== '') {
4353 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4361 initEvents: function()
4363 if (typeof (this.menu) != 'undefined') {
4364 this.menu.parentType = this.xtype;
4365 this.menu.triggerEl = this.el;
4366 this.menu = this.addxtype(Roo.apply({}, this.menu));
4369 this.el.select('a',true).on('click', this.onClick, this);
4371 if(this.tagtype == 'span'){
4372 this.el.select('span',true).on('click', this.onClick, this);
4375 // at this point parent should be available..
4376 this.parent().register(this);
4379 onClick : function(e)
4381 if (e.getTarget('.dropdown-menu-item')) {
4382 // did you click on a menu itemm.... - then don't trigger onclick..
4387 this.preventDefault ||
4390 Roo.log("NavItem - prevent Default?");
4394 if (this.disabled) {
4398 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4399 if (tg && tg.transition) {
4400 Roo.log("waiting for the transitionend");
4406 //Roo.log("fire event clicked");
4407 if(this.fireEvent('click', this, e) === false){
4411 if(this.tagtype == 'span'){
4415 //Roo.log(this.href);
4416 var ael = this.el.select('a',true).first();
4419 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4420 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4421 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4422 return; // ignore... - it's a 'hash' to another page.
4424 Roo.log("NavItem - prevent Default?");
4426 this.scrollToElement(e);
4430 var p = this.parent();
4432 if (['tabs','pills'].indexOf(p.type)!==-1) {
4433 if (typeof(p.setActiveItem) !== 'undefined') {
4434 p.setActiveItem(this);
4438 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4439 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4440 // remove the collapsed menu expand...
4441 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4445 isActive: function () {
4448 setActive : function(state, fire, is_was_active)
4450 if (this.active && !state && this.navId) {
4451 this.was_active = true;
4452 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4454 nv.clearWasActive(this);
4458 this.active = state;
4461 this.el.removeClass('active');
4462 } else if (!this.el.hasClass('active')) {
4463 this.el.addClass('active');
4466 this.fireEvent('changed', this, state);
4469 // show a panel if it's registered and related..
4471 if (!this.navId || !this.tabId || !state || is_was_active) {
4475 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4479 var pan = tg.getPanelByName(this.tabId);
4483 // if we can not flip to new panel - go back to old nav highlight..
4484 if (false == tg.showPanel(pan)) {
4485 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4487 var onav = nv.getWasActive();
4489 onav.setActive(true, false, true);
4498 // this should not be here...
4499 setDisabled : function(state)
4501 this.disabled = state;
4503 this.el.removeClass('disabled');
4504 } else if (!this.el.hasClass('disabled')) {
4505 this.el.addClass('disabled');
4511 * Fetch the element to display the tooltip on.
4512 * @return {Roo.Element} defaults to this.el
4514 tooltipEl : function()
4516 return this.el.select('' + this.tagtype + '', true).first();
4519 scrollToElement : function(e)
4521 var c = document.body;
4524 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4526 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4527 c = document.documentElement;
4530 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4536 var o = target.calcOffsetsTo(c);
4543 this.fireEvent('scrollto', this, options, e);
4545 Roo.get(c).scrollTo('top', options.value, true);
4558 * <span> icon </span>
4559 * <span> text </span>
4560 * <span>badge </span>
4564 * @class Roo.bootstrap.NavSidebarItem
4565 * @extends Roo.bootstrap.NavItem
4566 * Bootstrap Navbar.NavSidebarItem class
4567 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4568 * {bool} open is the menu open
4570 * Create a new Navbar Button
4571 * @param {Object} config The config object
4573 Roo.bootstrap.NavSidebarItem = function(config){
4574 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4579 * The raw click event for the entire grid.
4580 * @param {Roo.EventObject} e
4585 * Fires when the active item active state changes
4586 * @param {Roo.bootstrap.NavSidebarItem} this
4587 * @param {boolean} state the new state
4595 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4597 badgeWeight : 'default',
4601 getAutoCreate : function(){
4606 href : this.href || '#',
4618 html : this.html || ''
4623 cfg.cls += ' active';
4626 if (this.disabled) {
4627 cfg.cls += ' disabled';
4630 cfg.cls += ' open x-open';
4633 if (this.glyphicon || this.icon) {
4634 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4635 a.cn.push({ tag : 'i', cls : c }) ;
4640 if (this.badge !== '') {
4642 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4646 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4647 a.cls += 'dropdown-toggle treeview' ;
4655 initEvents : function()
4657 if (typeof (this.menu) != 'undefined') {
4658 this.menu.parentType = this.xtype;
4659 this.menu.triggerEl = this.el;
4660 this.menu = this.addxtype(Roo.apply({}, this.menu));
4663 this.el.on('click', this.onClick, this);
4666 if(this.badge !== ''){
4668 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4673 onClick : function(e)
4680 if(this.preventDefault){
4684 this.fireEvent('click', this);
4687 disable : function()
4689 this.setDisabled(true);
4694 this.setDisabled(false);
4697 setDisabled : function(state)
4699 if(this.disabled == state){
4703 this.disabled = state;
4706 this.el.addClass('disabled');
4710 this.el.removeClass('disabled');
4715 setActive : function(state)
4717 if(this.active == state){
4721 this.active = state;
4724 this.el.addClass('active');
4728 this.el.removeClass('active');
4733 isActive: function ()
4738 setBadge : function(str)
4744 this.badgeEl.dom.innerHTML = str;
4761 * @class Roo.bootstrap.Row
4762 * @extends Roo.bootstrap.Component
4763 * Bootstrap Row class (contains columns...)
4767 * @param {Object} config The config object
4770 Roo.bootstrap.Row = function(config){
4771 Roo.bootstrap.Row.superclass.constructor.call(this, config);
4774 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
4776 getAutoCreate : function(){
4795 * @class Roo.bootstrap.Element
4796 * @extends Roo.bootstrap.Component
4797 * Bootstrap Element class
4798 * @cfg {String} html contents of the element
4799 * @cfg {String} tag tag of the element
4800 * @cfg {String} cls class of the element
4801 * @cfg {Boolean} preventDefault (true|false) default false
4802 * @cfg {Boolean} clickable (true|false) default false
4805 * Create a new Element
4806 * @param {Object} config The config object
4809 Roo.bootstrap.Element = function(config){
4810 Roo.bootstrap.Element.superclass.constructor.call(this, config);
4816 * When a element is chick
4817 * @param {Roo.bootstrap.Element} this
4818 * @param {Roo.EventObject} e
4824 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
4829 preventDefault: false,
4832 getAutoCreate : function(){
4843 initEvents: function()
4845 Roo.bootstrap.Element.superclass.initEvents.call(this);
4848 this.el.on('click', this.onClick, this);
4853 onClick : function(e)
4855 if(this.preventDefault){
4859 this.fireEvent('click', this, e);
4862 getValue : function()
4864 return this.el.dom.innerHTML;
4867 setValue : function(value)
4869 this.el.dom.innerHTML = value;
4884 * @class Roo.bootstrap.Pagination
4885 * @extends Roo.bootstrap.Component
4886 * Bootstrap Pagination class
4887 * @cfg {String} size xs | sm | md | lg
4888 * @cfg {Boolean} inverse false | true
4891 * Create a new Pagination
4892 * @param {Object} config The config object
4895 Roo.bootstrap.Pagination = function(config){
4896 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4899 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
4905 getAutoCreate : function(){
4911 cfg.cls += ' inverse';
4917 cfg.cls += " " + this.cls;
4935 * @class Roo.bootstrap.PaginationItem
4936 * @extends Roo.bootstrap.Component
4937 * Bootstrap PaginationItem class
4938 * @cfg {String} html text
4939 * @cfg {String} href the link
4940 * @cfg {Boolean} preventDefault (true | false) default true
4941 * @cfg {Boolean} active (true | false) default false
4942 * @cfg {Boolean} disabled default false
4946 * Create a new PaginationItem
4947 * @param {Object} config The config object
4951 Roo.bootstrap.PaginationItem = function(config){
4952 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4957 * The raw click event for the entire grid.
4958 * @param {Roo.EventObject} e
4964 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
4968 preventDefault: true,
4973 getAutoCreate : function(){
4979 href : this.href ? this.href : '#',
4980 html : this.html ? this.html : ''
4990 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4994 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5000 initEvents: function() {
5002 this.el.on('click', this.onClick, this);
5005 onClick : function(e)
5007 Roo.log('PaginationItem on click ');
5008 if(this.preventDefault){
5016 this.fireEvent('click', this, e);
5032 * @class Roo.bootstrap.Slider
5033 * @extends Roo.bootstrap.Component
5034 * Bootstrap Slider class
5037 * Create a new Slider
5038 * @param {Object} config The config object
5041 Roo.bootstrap.Slider = function(config){
5042 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5045 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
5047 getAutoCreate : function(){
5051 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5055 cls: 'ui-slider-handle ui-state-default ui-corner-all'
5067 * Ext JS Library 1.1.1
5068 * Copyright(c) 2006-2007, Ext JS, LLC.
5070 * Originally Released Under LGPL - original licence link has changed is not relivant.
5073 * <script type="text/javascript">
5078 * @class Roo.grid.ColumnModel
5079 * @extends Roo.util.Observable
5080 * This is the default implementation of a ColumnModel used by the Grid. It defines
5081 * the columns in the grid.
5084 var colModel = new Roo.grid.ColumnModel([
5085 {header: "Ticker", width: 60, sortable: true, locked: true},
5086 {header: "Company Name", width: 150, sortable: true},
5087 {header: "Market Cap.", width: 100, sortable: true},
5088 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5089 {header: "Employees", width: 100, sortable: true, resizable: false}
5094 * The config options listed for this class are options which may appear in each
5095 * individual column definition.
5096 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5098 * @param {Object} config An Array of column config objects. See this class's
5099 * config objects for details.
5101 Roo.grid.ColumnModel = function(config){
5103 * The config passed into the constructor
5105 this.config = config;
5108 // if no id, create one
5109 // if the column does not have a dataIndex mapping,
5110 // map it to the order it is in the config
5111 for(var i = 0, len = config.length; i < len; i++){
5113 if(typeof c.dataIndex == "undefined"){
5116 if(typeof c.renderer == "string"){
5117 c.renderer = Roo.util.Format[c.renderer];
5119 if(typeof c.id == "undefined"){
5122 if(c.editor && c.editor.xtype){
5123 c.editor = Roo.factory(c.editor, Roo.grid);
5125 if(c.editor && c.editor.isFormField){
5126 c.editor = new Roo.grid.GridEditor(c.editor);
5128 this.lookup[c.id] = c;
5132 * The width of columns which have no width specified (defaults to 100)
5135 this.defaultWidth = 100;
5138 * Default sortable of columns which have no sortable specified (defaults to false)
5141 this.defaultSortable = false;
5145 * @event widthchange
5146 * Fires when the width of a column changes.
5147 * @param {ColumnModel} this
5148 * @param {Number} columnIndex The column index
5149 * @param {Number} newWidth The new width
5151 "widthchange": true,
5153 * @event headerchange
5154 * Fires when the text of a header changes.
5155 * @param {ColumnModel} this
5156 * @param {Number} columnIndex The column index
5157 * @param {Number} newText The new header text
5159 "headerchange": true,
5161 * @event hiddenchange
5162 * Fires when a column is hidden or "unhidden".
5163 * @param {ColumnModel} this
5164 * @param {Number} columnIndex The column index
5165 * @param {Boolean} hidden true if hidden, false otherwise
5167 "hiddenchange": true,
5169 * @event columnmoved
5170 * Fires when a column is moved.
5171 * @param {ColumnModel} this
5172 * @param {Number} oldIndex
5173 * @param {Number} newIndex
5175 "columnmoved" : true,
5177 * @event columlockchange
5178 * Fires when a column's locked state is changed
5179 * @param {ColumnModel} this
5180 * @param {Number} colIndex
5181 * @param {Boolean} locked true if locked
5183 "columnlockchange" : true
5185 Roo.grid.ColumnModel.superclass.constructor.call(this);
5187 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5189 * @cfg {String} header The header text to display in the Grid view.
5192 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5193 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5194 * specified, the column's index is used as an index into the Record's data Array.
5197 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5198 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5201 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5202 * Defaults to the value of the {@link #defaultSortable} property.
5203 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5206 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5209 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5212 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5215 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5218 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5219 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5220 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5221 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5224 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5227 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5230 * @cfg {String} cursor (Optional)
5233 * @cfg {String} tooltip (Optional)
5236 * @cfg {Number} xs (Optional)
5239 * @cfg {Number} sm (Optional)
5242 * @cfg {Number} md (Optional)
5245 * @cfg {Number} lg (Optional)
5248 * Returns the id of the column at the specified index.
5249 * @param {Number} index The column index
5250 * @return {String} the id
5252 getColumnId : function(index){
5253 return this.config[index].id;
5257 * Returns the column for a specified id.
5258 * @param {String} id The column id
5259 * @return {Object} the column
5261 getColumnById : function(id){
5262 return this.lookup[id];
5267 * Returns the column for a specified dataIndex.
5268 * @param {String} dataIndex The column dataIndex
5269 * @return {Object|Boolean} the column or false if not found
5271 getColumnByDataIndex: function(dataIndex){
5272 var index = this.findColumnIndex(dataIndex);
5273 return index > -1 ? this.config[index] : false;
5277 * Returns the index for a specified column id.
5278 * @param {String} id The column id
5279 * @return {Number} the index, or -1 if not found
5281 getIndexById : function(id){
5282 for(var i = 0, len = this.config.length; i < len; i++){
5283 if(this.config[i].id == id){
5291 * Returns the index for a specified column dataIndex.
5292 * @param {String} dataIndex The column dataIndex
5293 * @return {Number} the index, or -1 if not found
5296 findColumnIndex : function(dataIndex){
5297 for(var i = 0, len = this.config.length; i < len; i++){
5298 if(this.config[i].dataIndex == dataIndex){
5306 moveColumn : function(oldIndex, newIndex){
5307 var c = this.config[oldIndex];
5308 this.config.splice(oldIndex, 1);
5309 this.config.splice(newIndex, 0, c);
5310 this.dataMap = null;
5311 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5314 isLocked : function(colIndex){
5315 return this.config[colIndex].locked === true;
5318 setLocked : function(colIndex, value, suppressEvent){
5319 if(this.isLocked(colIndex) == value){
5322 this.config[colIndex].locked = value;
5324 this.fireEvent("columnlockchange", this, colIndex, value);
5328 getTotalLockedWidth : function(){
5330 for(var i = 0; i < this.config.length; i++){
5331 if(this.isLocked(i) && !this.isHidden(i)){
5332 this.totalWidth += this.getColumnWidth(i);
5338 getLockedCount : function(){
5339 for(var i = 0, len = this.config.length; i < len; i++){
5340 if(!this.isLocked(i)){
5345 return this.config.length;
5349 * Returns the number of columns.
5352 getColumnCount : function(visibleOnly){
5353 if(visibleOnly === true){
5355 for(var i = 0, len = this.config.length; i < len; i++){
5356 if(!this.isHidden(i)){
5362 return this.config.length;
5366 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5367 * @param {Function} fn
5368 * @param {Object} scope (optional)
5369 * @return {Array} result
5371 getColumnsBy : function(fn, scope){
5373 for(var i = 0, len = this.config.length; i < len; i++){
5374 var c = this.config[i];
5375 if(fn.call(scope||this, c, i) === true){
5383 * Returns true if the specified column is sortable.
5384 * @param {Number} col The column index
5387 isSortable : function(col){
5388 if(typeof this.config[col].sortable == "undefined"){
5389 return this.defaultSortable;
5391 return this.config[col].sortable;
5395 * Returns the rendering (formatting) function defined for the column.
5396 * @param {Number} col The column index.
5397 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5399 getRenderer : function(col){
5400 if(!this.config[col].renderer){
5401 return Roo.grid.ColumnModel.defaultRenderer;
5403 return this.config[col].renderer;
5407 * Sets the rendering (formatting) function for a column.
5408 * @param {Number} col The column index
5409 * @param {Function} fn The function to use to process the cell's raw data
5410 * to return HTML markup for the grid view. The render function is called with
5411 * the following parameters:<ul>
5412 * <li>Data value.</li>
5413 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5414 * <li>css A CSS style string to apply to the table cell.</li>
5415 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5416 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5417 * <li>Row index</li>
5418 * <li>Column index</li>
5419 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5421 setRenderer : function(col, fn){
5422 this.config[col].renderer = fn;
5426 * Returns the width for the specified column.
5427 * @param {Number} col The column index
5430 getColumnWidth : function(col){
5431 return this.config[col].width * 1 || this.defaultWidth;
5435 * Sets the width for a column.
5436 * @param {Number} col The column index
5437 * @param {Number} width The new width
5439 setColumnWidth : function(col, width, suppressEvent){
5440 this.config[col].width = width;
5441 this.totalWidth = null;
5443 this.fireEvent("widthchange", this, col, width);
5448 * Returns the total width of all columns.
5449 * @param {Boolean} includeHidden True to include hidden column widths
5452 getTotalWidth : function(includeHidden){
5453 if(!this.totalWidth){
5454 this.totalWidth = 0;
5455 for(var i = 0, len = this.config.length; i < len; i++){
5456 if(includeHidden || !this.isHidden(i)){
5457 this.totalWidth += this.getColumnWidth(i);
5461 return this.totalWidth;
5465 * Returns the header for the specified column.
5466 * @param {Number} col The column index
5469 getColumnHeader : function(col){
5470 return this.config[col].header;
5474 * Sets the header for a column.
5475 * @param {Number} col The column index
5476 * @param {String} header The new header
5478 setColumnHeader : function(col, header){
5479 this.config[col].header = header;
5480 this.fireEvent("headerchange", this, col, header);
5484 * Returns the tooltip for the specified column.
5485 * @param {Number} col The column index
5488 getColumnTooltip : function(col){
5489 return this.config[col].tooltip;
5492 * Sets the tooltip for a column.
5493 * @param {Number} col The column index
5494 * @param {String} tooltip The new tooltip
5496 setColumnTooltip : function(col, tooltip){
5497 this.config[col].tooltip = tooltip;
5501 * Returns the dataIndex for the specified column.
5502 * @param {Number} col The column index
5505 getDataIndex : function(col){
5506 return this.config[col].dataIndex;
5510 * Sets the dataIndex for a column.
5511 * @param {Number} col The column index
5512 * @param {Number} dataIndex The new dataIndex
5514 setDataIndex : function(col, dataIndex){
5515 this.config[col].dataIndex = dataIndex;
5521 * Returns true if the cell is editable.
5522 * @param {Number} colIndex The column index
5523 * @param {Number} rowIndex The row index - this is nto actually used..?
5526 isCellEditable : function(colIndex, rowIndex){
5527 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5531 * Returns the editor defined for the cell/column.
5532 * return false or null to disable editing.
5533 * @param {Number} colIndex The column index
5534 * @param {Number} rowIndex The row index
5537 getCellEditor : function(colIndex, rowIndex){
5538 return this.config[colIndex].editor;
5542 * Sets if a column is editable.
5543 * @param {Number} col The column index
5544 * @param {Boolean} editable True if the column is editable
5546 setEditable : function(col, editable){
5547 this.config[col].editable = editable;
5552 * Returns true if the column is hidden.
5553 * @param {Number} colIndex The column index
5556 isHidden : function(colIndex){
5557 return this.config[colIndex].hidden;
5562 * Returns true if the column width cannot be changed
5564 isFixed : function(colIndex){
5565 return this.config[colIndex].fixed;
5569 * Returns true if the column can be resized
5572 isResizable : function(colIndex){
5573 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5576 * Sets if a column is hidden.
5577 * @param {Number} colIndex The column index
5578 * @param {Boolean} hidden True if the column is hidden
5580 setHidden : function(colIndex, hidden){
5581 this.config[colIndex].hidden = hidden;
5582 this.totalWidth = null;
5583 this.fireEvent("hiddenchange", this, colIndex, hidden);
5587 * Sets the editor for a column.
5588 * @param {Number} col The column index
5589 * @param {Object} editor The editor object
5591 setEditor : function(col, editor){
5592 this.config[col].editor = editor;
5596 Roo.grid.ColumnModel.defaultRenderer = function(value)
5598 if(typeof value == "object") {
5601 if(typeof value == "string" && value.length < 1){
5605 return String.format("{0}", value);
5608 // Alias for backwards compatibility
5609 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5612 * Ext JS Library 1.1.1
5613 * Copyright(c) 2006-2007, Ext JS, LLC.
5615 * Originally Released Under LGPL - original licence link has changed is not relivant.
5618 * <script type="text/javascript">
5622 * @class Roo.LoadMask
5623 * A simple utility class for generically masking elements while loading data. If the element being masked has
5624 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5625 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5626 * element's UpdateManager load indicator and will be destroyed after the initial load.
5628 * Create a new LoadMask
5629 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5630 * @param {Object} config The config object
5632 Roo.LoadMask = function(el, config){
5633 this.el = Roo.get(el);
5634 Roo.apply(this, config);
5636 this.store.on('beforeload', this.onBeforeLoad, this);
5637 this.store.on('load', this.onLoad, this);
5638 this.store.on('loadexception', this.onLoadException, this);
5639 this.removeMask = false;
5641 var um = this.el.getUpdateManager();
5642 um.showLoadIndicator = false; // disable the default indicator
5643 um.on('beforeupdate', this.onBeforeLoad, this);
5644 um.on('update', this.onLoad, this);
5645 um.on('failure', this.onLoad, this);
5646 this.removeMask = true;
5650 Roo.LoadMask.prototype = {
5652 * @cfg {Boolean} removeMask
5653 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5654 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5658 * The text to display in a centered loading message box (defaults to 'Loading...')
5662 * @cfg {String} msgCls
5663 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5665 msgCls : 'x-mask-loading',
5668 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5674 * Disables the mask to prevent it from being displayed
5676 disable : function(){
5677 this.disabled = true;
5681 * Enables the mask so that it can be displayed
5683 enable : function(){
5684 this.disabled = false;
5687 onLoadException : function()
5691 if (typeof(arguments[3]) != 'undefined') {
5692 Roo.MessageBox.alert("Error loading",arguments[3]);
5696 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5697 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5704 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5709 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5713 onBeforeLoad : function(){
5715 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5720 destroy : function(){
5722 this.store.un('beforeload', this.onBeforeLoad, this);
5723 this.store.un('load', this.onLoad, this);
5724 this.store.un('loadexception', this.onLoadException, this);
5726 var um = this.el.getUpdateManager();
5727 um.un('beforeupdate', this.onBeforeLoad, this);
5728 um.un('update', this.onLoad, this);
5729 um.un('failure', this.onLoad, this);
5740 * @class Roo.bootstrap.Table
5741 * @extends Roo.bootstrap.Component
5742 * Bootstrap Table class
5743 * @cfg {String} cls table class
5744 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5745 * @cfg {String} bgcolor Specifies the background color for a table
5746 * @cfg {Number} border Specifies whether the table cells should have borders or not
5747 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5748 * @cfg {Number} cellspacing Specifies the space between cells
5749 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5750 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5751 * @cfg {String} sortable Specifies that the table should be sortable
5752 * @cfg {String} summary Specifies a summary of the content of a table
5753 * @cfg {Number} width Specifies the width of a table
5754 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5756 * @cfg {boolean} striped Should the rows be alternative striped
5757 * @cfg {boolean} bordered Add borders to the table
5758 * @cfg {boolean} hover Add hover highlighting
5759 * @cfg {boolean} condensed Format condensed
5760 * @cfg {boolean} responsive Format condensed
5761 * @cfg {Boolean} loadMask (true|false) default false
5762 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5763 * @cfg {Boolean} headerShow (true|false) generate thead, default true
5764 * @cfg {Boolean} rowSelection (true|false) default false
5765 * @cfg {Boolean} cellSelection (true|false) default false
5766 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5767 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
5768 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
5772 * Create a new Table
5773 * @param {Object} config The config object
5776 Roo.bootstrap.Table = function(config){
5777 Roo.bootstrap.Table.superclass.constructor.call(this, config);
5782 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5783 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5784 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5785 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5787 this.sm = this.sm || {xtype: 'RowSelectionModel'};
5789 this.sm.grid = this;
5790 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5791 this.sm = this.selModel;
5792 this.sm.xmodule = this.xmodule || false;
5795 if (this.cm && typeof(this.cm.config) == 'undefined') {
5796 this.colModel = new Roo.grid.ColumnModel(this.cm);
5797 this.cm = this.colModel;
5798 this.cm.xmodule = this.xmodule || false;
5801 this.store= Roo.factory(this.store, Roo.data);
5802 this.ds = this.store;
5803 this.ds.xmodule = this.xmodule || false;
5806 if (this.footer && this.store) {
5807 this.footer.dataSource = this.ds;
5808 this.footer = Roo.factory(this.footer);
5815 * Fires when a cell is clicked
5816 * @param {Roo.bootstrap.Table} this
5817 * @param {Roo.Element} el
5818 * @param {Number} rowIndex
5819 * @param {Number} columnIndex
5820 * @param {Roo.EventObject} e
5824 * @event celldblclick
5825 * Fires when a cell is double clicked
5826 * @param {Roo.bootstrap.Table} this
5827 * @param {Roo.Element} el
5828 * @param {Number} rowIndex
5829 * @param {Number} columnIndex
5830 * @param {Roo.EventObject} e
5832 "celldblclick" : true,
5835 * Fires when a row is clicked
5836 * @param {Roo.bootstrap.Table} this
5837 * @param {Roo.Element} el
5838 * @param {Number} rowIndex
5839 * @param {Roo.EventObject} e
5843 * @event rowdblclick
5844 * Fires when a row is double clicked
5845 * @param {Roo.bootstrap.Table} this
5846 * @param {Roo.Element} el
5847 * @param {Number} rowIndex
5848 * @param {Roo.EventObject} e
5850 "rowdblclick" : true,
5853 * Fires when a mouseover occur
5854 * @param {Roo.bootstrap.Table} this
5855 * @param {Roo.Element} el
5856 * @param {Number} rowIndex
5857 * @param {Number} columnIndex
5858 * @param {Roo.EventObject} e
5863 * Fires when a mouseout occur
5864 * @param {Roo.bootstrap.Table} this
5865 * @param {Roo.Element} el
5866 * @param {Number} rowIndex
5867 * @param {Number} columnIndex
5868 * @param {Roo.EventObject} e
5873 * Fires when a row is rendered, so you can change add a style to it.
5874 * @param {Roo.bootstrap.Table} this
5875 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
5879 * @event rowsrendered
5880 * Fires when all the rows have been rendered
5881 * @param {Roo.bootstrap.Table} this
5883 'rowsrendered' : true,
5885 * @event contextmenu
5886 * The raw contextmenu event for the entire grid.
5887 * @param {Roo.EventObject} e
5889 "contextmenu" : true,
5891 * @event rowcontextmenu
5892 * Fires when a row is right clicked
5893 * @param {Roo.bootstrap.Table} this
5894 * @param {Number} rowIndex
5895 * @param {Roo.EventObject} e
5897 "rowcontextmenu" : true,
5899 * @event cellcontextmenu
5900 * Fires when a cell is right clicked
5901 * @param {Roo.bootstrap.Table} this
5902 * @param {Number} rowIndex
5903 * @param {Number} cellIndex
5904 * @param {Roo.EventObject} e
5906 "cellcontextmenu" : true,
5908 * @event headercontextmenu
5909 * Fires when a header is right clicked
5910 * @param {Roo.bootstrap.Table} this
5911 * @param {Number} columnIndex
5912 * @param {Roo.EventObject} e
5914 "headercontextmenu" : true
5918 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
5944 rowSelection : false,
5945 cellSelection : false,
5948 // Roo.Element - the tbody
5950 // Roo.Element - thead element
5953 container: false, // used by gridpanel...
5957 getAutoCreate : function()
5959 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5966 if (this.scrollBody) {
5967 cfg.cls += ' table-body-fixed';
5970 cfg.cls += ' table-striped';
5974 cfg.cls += ' table-hover';
5976 if (this.bordered) {
5977 cfg.cls += ' table-bordered';
5979 if (this.condensed) {
5980 cfg.cls += ' table-condensed';
5982 if (this.responsive) {
5983 cfg.cls += ' table-responsive';
5987 cfg.cls+= ' ' +this.cls;
5990 // this lot should be simplifed...
5993 cfg.align=this.align;
5996 cfg.bgcolor=this.bgcolor;
5999 cfg.border=this.border;
6001 if (this.cellpadding) {
6002 cfg.cellpadding=this.cellpadding;
6004 if (this.cellspacing) {
6005 cfg.cellspacing=this.cellspacing;
6008 cfg.frame=this.frame;
6011 cfg.rules=this.rules;
6013 if (this.sortable) {
6014 cfg.sortable=this.sortable;
6017 cfg.summary=this.summary;
6020 cfg.width=this.width;
6023 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6026 if(this.store || this.cm){
6027 if(this.headerShow){
6028 cfg.cn.push(this.renderHeader());
6031 cfg.cn.push(this.renderBody());
6033 if(this.footerShow){
6034 cfg.cn.push(this.renderFooter());
6036 // where does this come from?
6037 //cfg.cls+= ' TableGrid';
6040 return { cn : [ cfg ] };
6043 initEvents : function()
6045 if(!this.store || !this.cm){
6048 if (this.selModel) {
6049 this.selModel.initEvents();
6053 //Roo.log('initEvents with ds!!!!');
6055 this.mainBody = this.el.select('tbody', true).first();
6056 this.mainHead = this.el.select('thead', true).first();
6063 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6064 e.on('click', _this.sort, _this);
6067 this.mainBody.on("click", this.onClick, this);
6068 this.mainBody.on("dblclick", this.onDblClick, this);
6070 // why is this done????? = it breaks dialogs??
6071 //this.parent().el.setStyle('position', 'relative');
6075 this.footer.parentId = this.id;
6076 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6079 this.el.select('tfoot tr td').first().addClass('hide');
6083 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6085 this.store.on('load', this.onLoad, this);
6086 this.store.on('beforeload', this.onBeforeLoad, this);
6087 this.store.on('update', this.onUpdate, this);
6088 this.store.on('add', this.onAdd, this);
6089 this.store.on("clear", this.clear, this);
6091 this.el.on("contextmenu", this.onContextMenu, this);
6093 this.mainBody.on('scroll', this.onBodyScroll, this);
6098 onContextMenu : function(e, t)
6100 this.processEvent("contextmenu", e);
6103 processEvent : function(name, e)
6105 if (name != 'touchstart' ) {
6106 this.fireEvent(name, e);
6109 var t = e.getTarget();
6111 var cell = Roo.get(t);
6117 if(cell.findParent('tfoot', false, true)){
6121 if(cell.findParent('thead', false, true)){
6123 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6124 cell = Roo.get(t).findParent('th', false, true);
6126 Roo.log("failed to find th in thead?");
6127 Roo.log(e.getTarget());
6132 var cellIndex = cell.dom.cellIndex;
6134 var ename = name == 'touchstart' ? 'click' : name;
6135 this.fireEvent("header" + ename, this, cellIndex, e);
6140 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6141 cell = Roo.get(t).findParent('td', false, true);
6143 Roo.log("failed to find th in tbody?");
6144 Roo.log(e.getTarget());
6149 var row = cell.findParent('tr', false, true);
6150 var cellIndex = cell.dom.cellIndex;
6151 var rowIndex = row.dom.rowIndex - 1;
6155 this.fireEvent("row" + name, this, rowIndex, e);
6159 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6165 onMouseover : function(e, el)
6167 var cell = Roo.get(el);
6173 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6174 cell = cell.findParent('td', false, true);
6177 var row = cell.findParent('tr', false, true);
6178 var cellIndex = cell.dom.cellIndex;
6179 var rowIndex = row.dom.rowIndex - 1; // start from 0
6181 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6185 onMouseout : function(e, el)
6187 var cell = Roo.get(el);
6193 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6194 cell = cell.findParent('td', false, true);
6197 var row = cell.findParent('tr', false, true);
6198 var cellIndex = cell.dom.cellIndex;
6199 var rowIndex = row.dom.rowIndex - 1; // start from 0
6201 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6205 onClick : function(e, el)
6207 var cell = Roo.get(el);
6209 if(!cell || (!this.cellSelection && !this.rowSelection)){
6213 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6214 cell = cell.findParent('td', false, true);
6217 if(!cell || typeof(cell) == 'undefined'){
6221 var row = cell.findParent('tr', false, true);
6223 if(!row || typeof(row) == 'undefined'){
6227 var cellIndex = cell.dom.cellIndex;
6228 var rowIndex = this.getRowIndex(row);
6230 // why??? - should these not be based on SelectionModel?
6231 if(this.cellSelection){
6232 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6235 if(this.rowSelection){
6236 this.fireEvent('rowclick', this, row, rowIndex, e);
6242 onDblClick : function(e,el)
6244 var cell = Roo.get(el);
6246 if(!cell || (!this.cellSelection && !this.rowSelection)){
6250 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6251 cell = cell.findParent('td', false, true);
6254 if(!cell || typeof(cell) == 'undefined'){
6258 var row = cell.findParent('tr', false, true);
6260 if(!row || typeof(row) == 'undefined'){
6264 var cellIndex = cell.dom.cellIndex;
6265 var rowIndex = this.getRowIndex(row);
6267 if(this.cellSelection){
6268 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6271 if(this.rowSelection){
6272 this.fireEvent('rowdblclick', this, row, rowIndex, e);
6276 sort : function(e,el)
6278 var col = Roo.get(el);
6280 if(!col.hasClass('sortable')){
6284 var sort = col.attr('sort');
6287 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6291 this.store.sortInfo = {field : sort, direction : dir};
6294 Roo.log("calling footer first");
6295 this.footer.onClick('first');
6298 this.store.load({ params : { start : 0 } });
6302 renderHeader : function()
6310 this.totalWidth = 0;
6312 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6314 var config = cm.config[i];
6319 html: cm.getColumnHeader(i)
6324 if(typeof(config.sortable) != 'undefined' && config.sortable){
6326 c.html = '<i class="glyphicon"></i>' + c.html;
6329 if(typeof(config.lgHeader) != 'undefined'){
6330 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6333 if(typeof(config.mdHeader) != 'undefined'){
6334 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6337 if(typeof(config.smHeader) != 'undefined'){
6338 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6341 if(typeof(config.xsHeader) != 'undefined'){
6342 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6349 if(typeof(config.tooltip) != 'undefined'){
6350 c.tooltip = config.tooltip;
6353 if(typeof(config.colspan) != 'undefined'){
6354 c.colspan = config.colspan;
6357 if(typeof(config.hidden) != 'undefined' && config.hidden){
6358 c.style += ' display:none;';
6361 if(typeof(config.dataIndex) != 'undefined'){
6362 c.sort = config.dataIndex;
6367 if(typeof(config.align) != 'undefined' && config.align.length){
6368 c.style += ' text-align:' + config.align + ';';
6371 if(typeof(config.width) != 'undefined'){
6372 c.style += ' width:' + config.width + 'px;';
6373 this.totalWidth += config.width;
6375 this.totalWidth += 100; // assume minimum of 100 per column?
6378 if(typeof(config.cls) != 'undefined'){
6379 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6382 ['xs','sm','md','lg'].map(function(size){
6384 if(typeof(config[size]) == 'undefined'){
6388 if (!config[size]) { // 0 = hidden
6389 c.cls += ' hidden-' + size;
6393 c.cls += ' col-' + size + '-' + config[size];
6403 renderBody : function()
6413 colspan : this.cm.getColumnCount()
6423 renderFooter : function()
6433 colspan : this.cm.getColumnCount()
6447 // Roo.log('ds onload');
6452 var ds = this.store;
6454 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6455 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6456 if (_this.store.sortInfo) {
6458 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6459 e.select('i', true).addClass(['glyphicon-arrow-up']);
6462 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6463 e.select('i', true).addClass(['glyphicon-arrow-down']);
6468 var tbody = this.mainBody;
6470 if(ds.getCount() > 0){
6471 ds.data.each(function(d,rowIndex){
6472 var row = this.renderRow(cm, ds, rowIndex);
6474 tbody.createChild(row);
6478 if(row.cellObjects.length){
6479 Roo.each(row.cellObjects, function(r){
6480 _this.renderCellObject(r);
6487 Roo.each(this.el.select('tbody td', true).elements, function(e){
6488 e.on('mouseover', _this.onMouseover, _this);
6491 Roo.each(this.el.select('tbody td', true).elements, function(e){
6492 e.on('mouseout', _this.onMouseout, _this);
6494 this.fireEvent('rowsrendered', this);
6495 //if(this.loadMask){
6496 // this.maskEl.hide();
6503 onUpdate : function(ds,record)
6505 this.refreshRow(record);
6509 onRemove : function(ds, record, index, isUpdate){
6510 if(isUpdate !== true){
6511 this.fireEvent("beforerowremoved", this, index, record);
6513 var bt = this.mainBody.dom;
6515 var rows = this.el.select('tbody > tr', true).elements;
6517 if(typeof(rows[index]) != 'undefined'){
6518 bt.removeChild(rows[index].dom);
6521 // if(bt.rows[index]){
6522 // bt.removeChild(bt.rows[index]);
6525 if(isUpdate !== true){
6526 //this.stripeRows(index);
6527 //this.syncRowHeights(index, index);
6529 this.fireEvent("rowremoved", this, index, record);
6533 onAdd : function(ds, records, rowIndex)
6535 //Roo.log('on Add called');
6536 // - note this does not handle multiple adding very well..
6537 var bt = this.mainBody.dom;
6538 for (var i =0 ; i < records.length;i++) {
6539 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6540 //Roo.log(records[i]);
6541 //Roo.log(this.store.getAt(rowIndex+i));
6542 this.insertRow(this.store, rowIndex + i, false);
6549 refreshRow : function(record){
6550 var ds = this.store, index;
6551 if(typeof record == 'number'){
6553 record = ds.getAt(index);
6555 index = ds.indexOf(record);
6557 this.insertRow(ds, index, true);
6559 this.onRemove(ds, record, index+1, true);
6561 //this.syncRowHeights(index, index);
6563 this.fireEvent("rowupdated", this, index, record);
6566 insertRow : function(dm, rowIndex, isUpdate){
6569 this.fireEvent("beforerowsinserted", this, rowIndex);
6571 //var s = this.getScrollState();
6572 var row = this.renderRow(this.cm, this.store, rowIndex);
6573 // insert before rowIndex..
6574 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6578 if(row.cellObjects.length){
6579 Roo.each(row.cellObjects, function(r){
6580 _this.renderCellObject(r);
6585 this.fireEvent("rowsinserted", this, rowIndex);
6586 //this.syncRowHeights(firstRow, lastRow);
6587 //this.stripeRows(firstRow);
6594 getRowDom : function(rowIndex)
6596 var rows = this.el.select('tbody > tr', true).elements;
6598 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6601 // returns the object tree for a tr..
6604 renderRow : function(cm, ds, rowIndex)
6607 var d = ds.getAt(rowIndex);
6614 var cellObjects = [];
6616 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6617 var config = cm.config[i];
6619 var renderer = cm.getRenderer(i);
6623 if(typeof(renderer) !== 'undefined'){
6624 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6626 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6627 // and are rendered into the cells after the row is rendered - using the id for the element.
6629 if(typeof(value) === 'object'){
6639 rowIndex : rowIndex,
6644 this.fireEvent('rowclass', this, rowcfg);
6648 cls : rowcfg.rowClass,
6650 html: (typeof(value) === 'object') ? '' : value
6657 if(typeof(config.colspan) != 'undefined'){
6658 td.colspan = config.colspan;
6661 if(typeof(config.hidden) != 'undefined' && config.hidden){
6662 td.style += ' display:none;';
6665 if(typeof(config.align) != 'undefined' && config.align.length){
6666 td.style += ' text-align:' + config.align + ';';
6669 if(typeof(config.width) != 'undefined'){
6670 td.style += ' width:' + config.width + 'px;';
6673 if(typeof(config.cursor) != 'undefined'){
6674 td.style += ' cursor:' + config.cursor + ';';
6677 if(typeof(config.cls) != 'undefined'){
6678 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6681 ['xs','sm','md','lg'].map(function(size){
6683 if(typeof(config[size]) == 'undefined'){
6687 if (!config[size]) { // 0 = hidden
6688 td.cls += ' hidden-' + size;
6692 td.cls += ' col-' + size + '-' + config[size];
6700 row.cellObjects = cellObjects;
6708 onBeforeLoad : function()
6710 //Roo.log('ds onBeforeLoad');
6714 //if(this.loadMask){
6715 // this.maskEl.show();
6723 this.el.select('tbody', true).first().dom.innerHTML = '';
6726 * Show or hide a row.
6727 * @param {Number} rowIndex to show or hide
6728 * @param {Boolean} state hide
6730 setRowVisibility : function(rowIndex, state)
6732 var bt = this.mainBody.dom;
6734 var rows = this.el.select('tbody > tr', true).elements;
6736 if(typeof(rows[rowIndex]) == 'undefined'){
6739 rows[rowIndex].dom.style.display = state ? '' : 'none';
6743 getSelectionModel : function(){
6745 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6747 return this.selModel;
6750 * Render the Roo.bootstrap object from renderder
6752 renderCellObject : function(r)
6756 var t = r.cfg.render(r.container);
6759 Roo.each(r.cfg.cn, function(c){
6761 container: t.getChildContainer(),
6764 _this.renderCellObject(child);
6769 getRowIndex : function(row)
6773 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6784 * Returns the grid's underlying element = used by panel.Grid
6785 * @return {Element} The element
6787 getGridEl : function(){
6791 * Forces a resize - used by panel.Grid
6792 * @return {Element} The element
6794 autoSize : function()
6796 //var ctr = Roo.get(this.container.dom.parentElement);
6797 var ctr = Roo.get(this.el.dom);
6799 var thd = this.getGridEl().select('thead',true).first();
6800 var tbd = this.getGridEl().select('tbody', true).first();
6801 var tfd = this.getGridEl().select('tfoot', true).first();
6803 var cw = ctr.getWidth();
6807 tbd.setSize(ctr.getWidth(),
6808 ctr.getHeight() - (thd.getHeight() + (tfd ? tfd.getHeight() : 0))
6810 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6813 cw = Math.max(cw, this.totalWidth);
6814 this.getGridEl().select('tr',true).setWidth(cw);
6815 // resize 'expandable coloumn?
6817 return; // we doe not have a view in this design..
6820 onBodyScroll: function()
6822 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6823 this.mainHead.setStyle({
6824 'position' : 'relative',
6825 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6830 var scrollHeight = this.mainBody.dom.scrollHeight;
6832 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
6834 var height = this.mainBody.getHeight();
6836 if(scrollHeight - height == scrollTop) {
6838 var total = this.ds.getTotalCount();
6840 if(this.footer.cursor + this.footer.pageSize < total){
6842 this.footer.ds.load({
6844 start : this.footer.cursor + this.footer.pageSize,
6845 limit : this.footer.pageSize
6866 * @class Roo.bootstrap.TableCell
6867 * @extends Roo.bootstrap.Component
6868 * Bootstrap TableCell class
6869 * @cfg {String} html cell contain text
6870 * @cfg {String} cls cell class
6871 * @cfg {String} tag cell tag (td|th) default td
6872 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6873 * @cfg {String} align Aligns the content in a cell
6874 * @cfg {String} axis Categorizes cells
6875 * @cfg {String} bgcolor Specifies the background color of a cell
6876 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6877 * @cfg {Number} colspan Specifies the number of columns a cell should span
6878 * @cfg {String} headers Specifies one or more header cells a cell is related to
6879 * @cfg {Number} height Sets the height of a cell
6880 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6881 * @cfg {Number} rowspan Sets the number of rows a cell should span
6882 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6883 * @cfg {String} valign Vertical aligns the content in a cell
6884 * @cfg {Number} width Specifies the width of a cell
6887 * Create a new TableCell
6888 * @param {Object} config The config object
6891 Roo.bootstrap.TableCell = function(config){
6892 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6895 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
6915 getAutoCreate : function(){
6916 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6936 cfg.align=this.align
6942 cfg.bgcolor=this.bgcolor
6945 cfg.charoff=this.charoff
6948 cfg.colspan=this.colspan
6951 cfg.headers=this.headers
6954 cfg.height=this.height
6957 cfg.nowrap=this.nowrap
6960 cfg.rowspan=this.rowspan
6963 cfg.scope=this.scope
6966 cfg.valign=this.valign
6969 cfg.width=this.width
6988 * @class Roo.bootstrap.TableRow
6989 * @extends Roo.bootstrap.Component
6990 * Bootstrap TableRow class
6991 * @cfg {String} cls row class
6992 * @cfg {String} align Aligns the content in a table row
6993 * @cfg {String} bgcolor Specifies a background color for a table row
6994 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6995 * @cfg {String} valign Vertical aligns the content in a table row
6998 * Create a new TableRow
6999 * @param {Object} config The config object
7002 Roo.bootstrap.TableRow = function(config){
7003 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7006 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
7014 getAutoCreate : function(){
7015 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7025 cfg.align = this.align;
7028 cfg.bgcolor = this.bgcolor;
7031 cfg.charoff = this.charoff;
7034 cfg.valign = this.valign;
7052 * @class Roo.bootstrap.TableBody
7053 * @extends Roo.bootstrap.Component
7054 * Bootstrap TableBody class
7055 * @cfg {String} cls element class
7056 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7057 * @cfg {String} align Aligns the content inside the element
7058 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7059 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7062 * Create a new TableBody
7063 * @param {Object} config The config object
7066 Roo.bootstrap.TableBody = function(config){
7067 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7070 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
7078 getAutoCreate : function(){
7079 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7093 cfg.align = this.align;
7096 cfg.charoff = this.charoff;
7099 cfg.valign = this.valign;
7106 // initEvents : function()
7113 // this.store = Roo.factory(this.store, Roo.data);
7114 // this.store.on('load', this.onLoad, this);
7116 // this.store.load();
7120 // onLoad: function ()
7122 // this.fireEvent('load', this);
7132 * Ext JS Library 1.1.1
7133 * Copyright(c) 2006-2007, Ext JS, LLC.
7135 * Originally Released Under LGPL - original licence link has changed is not relivant.
7138 * <script type="text/javascript">
7141 // as we use this in bootstrap.
7142 Roo.namespace('Roo.form');
7144 * @class Roo.form.Action
7145 * Internal Class used to handle form actions
7147 * @param {Roo.form.BasicForm} el The form element or its id
7148 * @param {Object} config Configuration options
7153 // define the action interface
7154 Roo.form.Action = function(form, options){
7156 this.options = options || {};
7159 * Client Validation Failed
7162 Roo.form.Action.CLIENT_INVALID = 'client';
7164 * Server Validation Failed
7167 Roo.form.Action.SERVER_INVALID = 'server';
7169 * Connect to Server Failed
7172 Roo.form.Action.CONNECT_FAILURE = 'connect';
7174 * Reading Data from Server Failed
7177 Roo.form.Action.LOAD_FAILURE = 'load';
7179 Roo.form.Action.prototype = {
7181 failureType : undefined,
7182 response : undefined,
7186 run : function(options){
7191 success : function(response){
7196 handleResponse : function(response){
7200 // default connection failure
7201 failure : function(response){
7203 this.response = response;
7204 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7205 this.form.afterAction(this, false);
7208 processResponse : function(response){
7209 this.response = response;
7210 if(!response.responseText){
7213 this.result = this.handleResponse(response);
7217 // utility functions used internally
7218 getUrl : function(appendParams){
7219 var url = this.options.url || this.form.url || this.form.el.dom.action;
7221 var p = this.getParams();
7223 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7229 getMethod : function(){
7230 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7233 getParams : function(){
7234 var bp = this.form.baseParams;
7235 var p = this.options.params;
7237 if(typeof p == "object"){
7238 p = Roo.urlEncode(Roo.applyIf(p, bp));
7239 }else if(typeof p == 'string' && bp){
7240 p += '&' + Roo.urlEncode(bp);
7243 p = Roo.urlEncode(bp);
7248 createCallback : function(){
7250 success: this.success,
7251 failure: this.failure,
7253 timeout: (this.form.timeout*1000),
7254 upload: this.form.fileUpload ? this.success : undefined
7259 Roo.form.Action.Submit = function(form, options){
7260 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7263 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7266 haveProgress : false,
7267 uploadComplete : false,
7269 // uploadProgress indicator.
7270 uploadProgress : function()
7272 if (!this.form.progressUrl) {
7276 if (!this.haveProgress) {
7277 Roo.MessageBox.progress("Uploading", "Uploading");
7279 if (this.uploadComplete) {
7280 Roo.MessageBox.hide();
7284 this.haveProgress = true;
7286 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7288 var c = new Roo.data.Connection();
7290 url : this.form.progressUrl,
7295 success : function(req){
7296 //console.log(data);
7300 rdata = Roo.decode(req.responseText)
7302 Roo.log("Invalid data from server..");
7306 if (!rdata || !rdata.success) {
7308 Roo.MessageBox.alert(Roo.encode(rdata));
7311 var data = rdata.data;
7313 if (this.uploadComplete) {
7314 Roo.MessageBox.hide();
7319 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7320 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7323 this.uploadProgress.defer(2000,this);
7326 failure: function(data) {
7327 Roo.log('progress url failed ');
7338 // run get Values on the form, so it syncs any secondary forms.
7339 this.form.getValues();
7341 var o = this.options;
7342 var method = this.getMethod();
7343 var isPost = method == 'POST';
7344 if(o.clientValidation === false || this.form.isValid()){
7346 if (this.form.progressUrl) {
7347 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7348 (new Date() * 1) + '' + Math.random());
7353 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7354 form:this.form.el.dom,
7355 url:this.getUrl(!isPost),
7357 params:isPost ? this.getParams() : null,
7358 isUpload: this.form.fileUpload
7361 this.uploadProgress();
7363 }else if (o.clientValidation !== false){ // client validation failed
7364 this.failureType = Roo.form.Action.CLIENT_INVALID;
7365 this.form.afterAction(this, false);
7369 success : function(response)
7371 this.uploadComplete= true;
7372 if (this.haveProgress) {
7373 Roo.MessageBox.hide();
7377 var result = this.processResponse(response);
7378 if(result === true || result.success){
7379 this.form.afterAction(this, true);
7383 this.form.markInvalid(result.errors);
7384 this.failureType = Roo.form.Action.SERVER_INVALID;
7386 this.form.afterAction(this, false);
7388 failure : function(response)
7390 this.uploadComplete= true;
7391 if (this.haveProgress) {
7392 Roo.MessageBox.hide();
7395 this.response = response;
7396 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7397 this.form.afterAction(this, false);
7400 handleResponse : function(response){
7401 if(this.form.errorReader){
7402 var rs = this.form.errorReader.read(response);
7405 for(var i = 0, len = rs.records.length; i < len; i++) {
7406 var r = rs.records[i];
7410 if(errors.length < 1){
7414 success : rs.success,
7420 ret = Roo.decode(response.responseText);
7424 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7434 Roo.form.Action.Load = function(form, options){
7435 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7436 this.reader = this.form.reader;
7439 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7444 Roo.Ajax.request(Roo.apply(
7445 this.createCallback(), {
7446 method:this.getMethod(),
7447 url:this.getUrl(false),
7448 params:this.getParams()
7452 success : function(response){
7454 var result = this.processResponse(response);
7455 if(result === true || !result.success || !result.data){
7456 this.failureType = Roo.form.Action.LOAD_FAILURE;
7457 this.form.afterAction(this, false);
7460 this.form.clearInvalid();
7461 this.form.setValues(result.data);
7462 this.form.afterAction(this, true);
7465 handleResponse : function(response){
7466 if(this.form.reader){
7467 var rs = this.form.reader.read(response);
7468 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7470 success : rs.success,
7474 return Roo.decode(response.responseText);
7478 Roo.form.Action.ACTION_TYPES = {
7479 'load' : Roo.form.Action.Load,
7480 'submit' : Roo.form.Action.Submit
7489 * @class Roo.bootstrap.Form
7490 * @extends Roo.bootstrap.Component
7491 * Bootstrap Form class
7492 * @cfg {String} method GET | POST (default POST)
7493 * @cfg {String} labelAlign top | left (default top)
7494 * @cfg {String} align left | right - for navbars
7495 * @cfg {Boolean} loadMask load mask when submit (default true)
7500 * @param {Object} config The config object
7504 Roo.bootstrap.Form = function(config){
7505 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7507 Roo.bootstrap.Form.popover.apply();
7511 * @event clientvalidation
7512 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7513 * @param {Form} this
7514 * @param {Boolean} valid true if the form has passed client-side validation
7516 clientvalidation: true,
7518 * @event beforeaction
7519 * Fires before any action is performed. Return false to cancel the action.
7520 * @param {Form} this
7521 * @param {Action} action The action to be performed
7525 * @event actionfailed
7526 * Fires when an action fails.
7527 * @param {Form} this
7528 * @param {Action} action The action that failed
7530 actionfailed : true,
7532 * @event actioncomplete
7533 * Fires when an action is completed.
7534 * @param {Form} this
7535 * @param {Action} action The action that completed
7537 actioncomplete : true
7542 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7545 * @cfg {String} method
7546 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7551 * The URL to use for form actions if one isn't supplied in the action options.
7554 * @cfg {Boolean} fileUpload
7555 * Set to true if this form is a file upload.
7559 * @cfg {Object} baseParams
7560 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7564 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7568 * @cfg {Sting} align (left|right) for navbar forms
7573 activeAction : null,
7576 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7577 * element by passing it or its id or mask the form itself by passing in true.
7580 waitMsgTarget : false,
7585 * @cfg {Boolean} errorMask (true|false) default false
7590 * @cfg {Number} maskOffset Default 100
7594 getAutoCreate : function(){
7598 method : this.method || 'POST',
7599 id : this.id || Roo.id(),
7602 if (this.parent().xtype.match(/^Nav/)) {
7603 cfg.cls = 'navbar-form navbar-' + this.align;
7607 if (this.labelAlign == 'left' ) {
7608 cfg.cls += ' form-horizontal';
7614 initEvents : function()
7616 this.el.on('submit', this.onSubmit, this);
7617 // this was added as random key presses on the form where triggering form submit.
7618 this.el.on('keypress', function(e) {
7619 if (e.getCharCode() != 13) {
7622 // we might need to allow it for textareas.. and some other items.
7623 // check e.getTarget().
7625 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7629 Roo.log("keypress blocked");
7637 onSubmit : function(e){
7642 * Returns true if client-side validation on the form is successful.
7645 isValid : function(){
7646 var items = this.getItems();
7650 items.each(function(f){
7657 if(!target && f.el.isVisible(true)){
7663 if(this.errorMask && !valid){
7664 Roo.bootstrap.Form.popover.mask(this, target);
7671 * Returns true if any fields in this form have changed since their original load.
7674 isDirty : function(){
7676 var items = this.getItems();
7677 items.each(function(f){
7687 * Performs a predefined action (submit or load) or custom actions you define on this form.
7688 * @param {String} actionName The name of the action type
7689 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
7690 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7691 * accept other config options):
7693 Property Type Description
7694 ---------------- --------------- ----------------------------------------------------------------------------------
7695 url String The url for the action (defaults to the form's url)
7696 method String The form method to use (defaults to the form's method, or POST if not defined)
7697 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
7698 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
7699 validate the form on the client (defaults to false)
7701 * @return {BasicForm} this
7703 doAction : function(action, options){
7704 if(typeof action == 'string'){
7705 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7707 if(this.fireEvent('beforeaction', this, action) !== false){
7708 this.beforeAction(action);
7709 action.run.defer(100, action);
7715 beforeAction : function(action){
7716 var o = action.options;
7719 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7721 // not really supported yet.. ??
7723 //if(this.waitMsgTarget === true){
7724 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7725 //}else if(this.waitMsgTarget){
7726 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7727 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7729 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7735 afterAction : function(action, success){
7736 this.activeAction = null;
7737 var o = action.options;
7739 //if(this.waitMsgTarget === true){
7741 //}else if(this.waitMsgTarget){
7742 // this.waitMsgTarget.unmask();
7744 // Roo.MessageBox.updateProgress(1);
7745 // Roo.MessageBox.hide();
7752 Roo.callback(o.success, o.scope, [this, action]);
7753 this.fireEvent('actioncomplete', this, action);
7757 // failure condition..
7758 // we have a scenario where updates need confirming.
7759 // eg. if a locking scenario exists..
7760 // we look for { errors : { needs_confirm : true }} in the response.
7762 (typeof(action.result) != 'undefined') &&
7763 (typeof(action.result.errors) != 'undefined') &&
7764 (typeof(action.result.errors.needs_confirm) != 'undefined')
7767 Roo.log("not supported yet");
7770 Roo.MessageBox.confirm(
7771 "Change requires confirmation",
7772 action.result.errorMsg,
7777 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
7787 Roo.callback(o.failure, o.scope, [this, action]);
7788 // show an error message if no failed handler is set..
7789 if (!this.hasListener('actionfailed')) {
7790 Roo.log("need to add dialog support");
7792 Roo.MessageBox.alert("Error",
7793 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7794 action.result.errorMsg :
7795 "Saving Failed, please check your entries or try again"
7800 this.fireEvent('actionfailed', this, action);
7805 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7806 * @param {String} id The value to search for
7809 findField : function(id){
7810 var items = this.getItems();
7811 var field = items.get(id);
7813 items.each(function(f){
7814 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7821 return field || null;
7824 * Mark fields in this form invalid in bulk.
7825 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7826 * @return {BasicForm} this
7828 markInvalid : function(errors){
7829 if(errors instanceof Array){
7830 for(var i = 0, len = errors.length; i < len; i++){
7831 var fieldError = errors[i];
7832 var f = this.findField(fieldError.id);
7834 f.markInvalid(fieldError.msg);
7840 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7841 field.markInvalid(errors[id]);
7845 //Roo.each(this.childForms || [], function (f) {
7846 // f.markInvalid(errors);
7853 * Set values for fields in this form in bulk.
7854 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7855 * @return {BasicForm} this
7857 setValues : function(values){
7858 if(values instanceof Array){ // array of objects
7859 for(var i = 0, len = values.length; i < len; i++){
7861 var f = this.findField(v.id);
7863 f.setValue(v.value);
7864 if(this.trackResetOnLoad){
7865 f.originalValue = f.getValue();
7869 }else{ // object hash
7872 if(typeof values[id] != 'function' && (field = this.findField(id))){
7874 if (field.setFromData &&
7876 field.displayField &&
7877 // combos' with local stores can
7878 // be queried via setValue()
7879 // to set their value..
7880 (field.store && !field.store.isLocal)
7884 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7885 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7886 field.setFromData(sd);
7889 field.setValue(values[id]);
7893 if(this.trackResetOnLoad){
7894 field.originalValue = field.getValue();
7900 //Roo.each(this.childForms || [], function (f) {
7901 // f.setValues(values);
7908 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7909 * they are returned as an array.
7910 * @param {Boolean} asString
7913 getValues : function(asString){
7914 //if (this.childForms) {
7915 // copy values from the child forms
7916 // Roo.each(this.childForms, function (f) {
7917 // this.setValues(f.getValues());
7923 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7924 if(asString === true){
7927 return Roo.urlDecode(fs);
7931 * Returns the fields in this form as an object with key/value pairs.
7932 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7935 getFieldValues : function(with_hidden)
7937 var items = this.getItems();
7939 items.each(function(f){
7943 var v = f.getValue();
7944 if (f.inputType =='radio') {
7945 if (typeof(ret[f.getName()]) == 'undefined') {
7946 ret[f.getName()] = ''; // empty..
7949 if (!f.el.dom.checked) {
7957 // not sure if this supported any more..
7958 if ((typeof(v) == 'object') && f.getRawValue) {
7959 v = f.getRawValue() ; // dates..
7961 // combo boxes where name != hiddenName...
7962 if (f.name !== false && f.name != '' && f.name != f.getName()) {
7963 ret[f.name] = f.getRawValue();
7965 ret[f.getName()] = v;
7972 * Clears all invalid messages in this form.
7973 * @return {BasicForm} this
7975 clearInvalid : function(){
7976 var items = this.getItems();
7978 items.each(function(f){
7989 * @return {BasicForm} this
7992 var items = this.getItems();
7993 items.each(function(f){
7997 Roo.each(this.childForms || [], function (f) {
8004 getItems : function()
8006 var r=new Roo.util.MixedCollection(false, function(o){
8007 return o.id || (o.id = Roo.id());
8009 var iter = function(el) {
8016 Roo.each(el.items,function(e) {
8033 Roo.apply(Roo.bootstrap.Form, {
8060 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8061 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8062 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8063 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8066 this.maskEl.top.enableDisplayMode("block");
8067 this.maskEl.left.enableDisplayMode("block");
8068 this.maskEl.bottom.enableDisplayMode("block");
8069 this.maskEl.right.enableDisplayMode("block");
8071 this.toolTip = new Roo.bootstrap.Tooltip({
8072 cls : 'roo-form-error-popover',
8074 'left' : ['r-l', [-2,0], 'right'],
8075 'right' : ['l-r', [2,0], 'left'],
8076 'bottom' : ['tl-bl', [0,2], 'top'],
8077 'top' : [ 'bl-tl', [0,-2], 'bottom']
8081 this.toolTip.render(Roo.get(document.body));
8083 this.toolTip.el.enableDisplayMode("block");
8085 Roo.get(document.body).on('click', function(){
8089 Roo.get(document.body).on('touchstart', function(){
8093 this.isApplied = true
8096 mask : function(form, target)
8100 this.target = target;
8102 if(!this.form.errorMask || !target.el){
8106 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8108 var ot = this.target.el.calcOffsetsTo(scrollable);
8110 var scrollTo = ot[1] - this.form.maskOffset;
8112 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8114 scrollable.scrollTo('top', scrollTo);
8116 var box = this.target.el.getBox();
8118 var zIndex = Roo.bootstrap.Modal.zIndex++;
8121 this.maskEl.top.setStyle('position', 'absolute');
8122 this.maskEl.top.setStyle('z-index', zIndex);
8123 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8124 this.maskEl.top.setLeft(0);
8125 this.maskEl.top.setTop(0);
8126 this.maskEl.top.show();
8128 this.maskEl.left.setStyle('position', 'absolute');
8129 this.maskEl.left.setStyle('z-index', zIndex);
8130 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8131 this.maskEl.left.setLeft(0);
8132 this.maskEl.left.setTop(box.y - this.padding);
8133 this.maskEl.left.show();
8135 this.maskEl.bottom.setStyle('position', 'absolute');
8136 this.maskEl.bottom.setStyle('z-index', zIndex);
8137 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8138 this.maskEl.bottom.setLeft(0);
8139 this.maskEl.bottom.setTop(box.bottom + this.padding);
8140 this.maskEl.bottom.show();
8142 this.maskEl.right.setStyle('position', 'absolute');
8143 this.maskEl.right.setStyle('z-index', zIndex);
8144 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8145 this.maskEl.right.setLeft(box.right + this.padding);
8146 this.maskEl.right.setTop(box.y - this.padding);
8147 this.maskEl.right.show();
8149 this.toolTip.bindEl = this.target.el;
8151 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8153 var tip = this.target.blankText;
8155 if(this.target.getValue() !== '' ) {
8157 if (this.target.invalidText.length) {
8158 tip = this.target.invalidText;
8159 } else if (this.target.regexText.length){
8160 tip = this.target.regexText;
8164 this.toolTip.show(tip);
8166 this.intervalID = window.setInterval(function() {
8167 Roo.bootstrap.Form.popover.unmask();
8170 window.onwheel = function(){ return false;};
8172 (function(){ this.isMasked = true; }).defer(500, this);
8178 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8182 this.maskEl.top.setStyle('position', 'absolute');
8183 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8184 this.maskEl.top.hide();
8186 this.maskEl.left.setStyle('position', 'absolute');
8187 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8188 this.maskEl.left.hide();
8190 this.maskEl.bottom.setStyle('position', 'absolute');
8191 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8192 this.maskEl.bottom.hide();
8194 this.maskEl.right.setStyle('position', 'absolute');
8195 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8196 this.maskEl.right.hide();
8198 this.toolTip.hide();
8200 this.toolTip.el.hide();
8202 window.onwheel = function(){ return true;};
8204 if(this.intervalID){
8205 window.clearInterval(this.intervalID);
8206 this.intervalID = false;
8209 this.isMasked = false;
8219 * Ext JS Library 1.1.1
8220 * Copyright(c) 2006-2007, Ext JS, LLC.
8222 * Originally Released Under LGPL - original licence link has changed is not relivant.
8225 * <script type="text/javascript">
8228 * @class Roo.form.VTypes
8229 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8232 Roo.form.VTypes = function(){
8233 // closure these in so they are only created once.
8234 var alpha = /^[a-zA-Z_]+$/;
8235 var alphanum = /^[a-zA-Z0-9_]+$/;
8236 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8237 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8239 // All these messages and functions are configurable
8242 * The function used to validate email addresses
8243 * @param {String} value The email address
8245 'email' : function(v){
8246 return email.test(v);
8249 * The error text to display when the email validation function returns false
8252 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8254 * The keystroke filter mask to be applied on email input
8257 'emailMask' : /[a-z0-9_\.\-@]/i,
8260 * The function used to validate URLs
8261 * @param {String} value The URL
8263 'url' : function(v){
8267 * The error text to display when the url validation function returns false
8270 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8273 * The function used to validate alpha values
8274 * @param {String} value The value
8276 'alpha' : function(v){
8277 return alpha.test(v);
8280 * The error text to display when the alpha validation function returns false
8283 'alphaText' : 'This field should only contain letters and _',
8285 * The keystroke filter mask to be applied on alpha input
8288 'alphaMask' : /[a-z_]/i,
8291 * The function used to validate alphanumeric values
8292 * @param {String} value The value
8294 'alphanum' : function(v){
8295 return alphanum.test(v);
8298 * The error text to display when the alphanumeric validation function returns false
8301 'alphanumText' : 'This field should only contain letters, numbers and _',
8303 * The keystroke filter mask to be applied on alphanumeric input
8306 'alphanumMask' : /[a-z0-9_]/i
8316 * @class Roo.bootstrap.Input
8317 * @extends Roo.bootstrap.Component
8318 * Bootstrap Input class
8319 * @cfg {Boolean} disabled is it disabled
8320 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8321 * @cfg {String} name name of the input
8322 * @cfg {string} fieldLabel - the label associated
8323 * @cfg {string} placeholder - placeholder to put in text.
8324 * @cfg {string} before - input group add on before
8325 * @cfg {string} after - input group add on after
8326 * @cfg {string} size - (lg|sm) or leave empty..
8327 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8328 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8329 * @cfg {Number} md colspan out of 12 for computer-sized screens
8330 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8331 * @cfg {string} value default value of the input
8332 * @cfg {Number} labelWidth set the width of label
8333 * @cfg {Number} labellg set the width of label (1-12)
8334 * @cfg {Number} labelmd set the width of label (1-12)
8335 * @cfg {Number} labelsm set the width of label (1-12)
8336 * @cfg {Number} labelxs set the width of label (1-12)
8337 * @cfg {String} labelAlign (top|left)
8338 * @cfg {Boolean} readOnly Specifies that the field should be read-only
8339 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8340 * @cfg {String} indicatorpos (left|right) default left
8342 * @cfg {String} align (left|center|right) Default left
8343 * @cfg {Boolean} forceFeedback (true|false) Default false
8349 * Create a new Input
8350 * @param {Object} config The config object
8353 Roo.bootstrap.Input = function(config){
8355 Roo.bootstrap.Input.superclass.constructor.call(this, config);
8360 * Fires when this field receives input focus.
8361 * @param {Roo.form.Field} this
8366 * Fires when this field loses input focus.
8367 * @param {Roo.form.Field} this
8372 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8373 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8374 * @param {Roo.form.Field} this
8375 * @param {Roo.EventObject} e The event object
8380 * Fires just before the field blurs if the field value has changed.
8381 * @param {Roo.form.Field} this
8382 * @param {Mixed} newValue The new value
8383 * @param {Mixed} oldValue The original value
8388 * Fires after the field has been marked as invalid.
8389 * @param {Roo.form.Field} this
8390 * @param {String} msg The validation message
8395 * Fires after the field has been validated with no errors.
8396 * @param {Roo.form.Field} this
8401 * Fires after the key up
8402 * @param {Roo.form.Field} this
8403 * @param {Roo.EventObject} e The event Object
8409 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
8411 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8412 automatic validation (defaults to "keyup").
8414 validationEvent : "keyup",
8416 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8418 validateOnBlur : true,
8420 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8422 validationDelay : 250,
8424 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8426 focusClass : "x-form-focus", // not needed???
8430 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8432 invalidClass : "has-warning",
8435 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8437 validClass : "has-success",
8440 * @cfg {Boolean} hasFeedback (true|false) default true
8445 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8447 invalidFeedbackClass : "glyphicon-warning-sign",
8450 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8452 validFeedbackClass : "glyphicon-ok",
8455 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8457 selectOnFocus : false,
8460 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8464 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8469 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8471 disableKeyFilter : false,
8474 * @cfg {Boolean} disabled True to disable the field (defaults to false).
8478 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8482 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8484 blankText : "Please complete this mandatory field",
8487 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8491 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8493 maxLength : Number.MAX_VALUE,
8495 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8497 minLengthText : "The minimum length for this field is {0}",
8499 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8501 maxLengthText : "The maximum length for this field is {0}",
8505 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8506 * If available, this function will be called only after the basic validators all return true, and will be passed the
8507 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8511 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8512 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8513 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
8517 * @cfg {String} regexText -- Depricated - use Invalid Text
8522 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8528 autocomplete: false,
8547 formatedValue : false,
8548 forceFeedback : false,
8550 indicatorpos : 'left',
8557 parentLabelAlign : function()
8560 while (parent.parent()) {
8561 parent = parent.parent();
8562 if (typeof(parent.labelAlign) !='undefined') {
8563 return parent.labelAlign;
8570 getAutoCreate : function()
8572 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8578 if(this.inputType != 'hidden'){
8579 cfg.cls = 'form-group' //input-group
8585 type : this.inputType,
8587 cls : 'form-control',
8588 placeholder : this.placeholder || '',
8589 autocomplete : this.autocomplete || 'new-password'
8593 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8596 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8597 input.maxLength = this.maxLength;
8600 if (this.disabled) {
8601 input.disabled=true;
8604 if (this.readOnly) {
8605 input.readonly=true;
8609 input.name = this.name;
8613 input.cls += ' input-' + this.size;
8617 ['xs','sm','md','lg'].map(function(size){
8618 if (settings[size]) {
8619 cfg.cls += ' col-' + size + '-' + settings[size];
8623 var inputblock = input;
8627 cls: 'glyphicon form-control-feedback'
8630 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8633 cls : 'has-feedback',
8641 if (this.before || this.after) {
8644 cls : 'input-group',
8648 if (this.before && typeof(this.before) == 'string') {
8650 inputblock.cn.push({
8652 cls : 'roo-input-before input-group-addon',
8656 if (this.before && typeof(this.before) == 'object') {
8657 this.before = Roo.factory(this.before);
8659 inputblock.cn.push({
8661 cls : 'roo-input-before input-group-' +
8662 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8666 inputblock.cn.push(input);
8668 if (this.after && typeof(this.after) == 'string') {
8669 inputblock.cn.push({
8671 cls : 'roo-input-after input-group-addon',
8675 if (this.after && typeof(this.after) == 'object') {
8676 this.after = Roo.factory(this.after);
8678 inputblock.cn.push({
8680 cls : 'roo-input-after input-group-' +
8681 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8685 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8686 inputblock.cls += ' has-feedback';
8687 inputblock.cn.push(feedback);
8691 if (align ==='left' && this.fieldLabel.length) {
8693 cfg.cls += ' roo-form-group-label-left';
8698 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8699 tooltip : 'This field is required'
8704 cls : 'control-label',
8705 html : this.fieldLabel
8716 var labelCfg = cfg.cn[1];
8717 var contentCfg = cfg.cn[2];
8719 if(this.indicatorpos == 'right'){
8724 cls : 'control-label',
8725 html : this.fieldLabel
8730 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8731 tooltip : 'This field is required'
8742 labelCfg = cfg.cn[0];
8743 contentCfg = cfg.cn[2];
8747 if(this.labelWidth > 12){
8748 labelCfg.style = "width: " + this.labelWidth + 'px';
8751 if(this.labelWidth < 13 && this.labelmd == 0){
8752 this.labelmd = this.labelWidth;
8755 if(this.labellg > 0){
8756 labelCfg.cls += ' col-lg-' + this.labellg;
8757 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
8760 if(this.labelmd > 0){
8761 labelCfg.cls += ' col-md-' + this.labelmd;
8762 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
8765 if(this.labelsm > 0){
8766 labelCfg.cls += ' col-sm-' + this.labelsm;
8767 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
8770 if(this.labelxs > 0){
8771 labelCfg.cls += ' col-xs-' + this.labelxs;
8772 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
8776 } else if ( this.fieldLabel.length) {
8781 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8782 tooltip : 'This field is required'
8786 //cls : 'input-group-addon',
8787 html : this.fieldLabel
8795 if(this.indicatorpos == 'right'){
8800 //cls : 'input-group-addon',
8801 html : this.fieldLabel
8806 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8807 tooltip : 'This field is required'
8827 if (this.parentType === 'Navbar' && this.parent().bar) {
8828 cfg.cls += ' navbar-form';
8831 if (this.parentType === 'NavGroup') {
8832 cfg.cls += ' navbar-form';
8840 * return the real input element.
8842 inputEl: function ()
8844 return this.el.select('input.form-control',true).first();
8847 tooltipEl : function()
8849 return this.inputEl();
8852 indicatorEl : function()
8854 var indicator = this.el.select('i.roo-required-indicator',true).first();
8864 setDisabled : function(v)
8866 var i = this.inputEl().dom;
8868 i.removeAttribute('disabled');
8872 i.setAttribute('disabled','true');
8874 initEvents : function()
8877 this.inputEl().on("keydown" , this.fireKey, this);
8878 this.inputEl().on("focus", this.onFocus, this);
8879 this.inputEl().on("blur", this.onBlur, this);
8881 this.inputEl().relayEvent('keyup', this);
8883 this.indicator = this.indicatorEl();
8886 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
8887 this.indicator.hide();
8890 // reference to original value for reset
8891 this.originalValue = this.getValue();
8892 //Roo.form.TextField.superclass.initEvents.call(this);
8893 if(this.validationEvent == 'keyup'){
8894 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8895 this.inputEl().on('keyup', this.filterValidation, this);
8897 else if(this.validationEvent !== false){
8898 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8901 if(this.selectOnFocus){
8902 this.on("focus", this.preFocus, this);
8905 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8906 this.inputEl().on("keypress", this.filterKeys, this);
8908 this.inputEl().relayEvent('keypress', this);
8911 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
8912 this.el.on("click", this.autoSize, this);
8915 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8916 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8919 if (typeof(this.before) == 'object') {
8920 this.before.render(this.el.select('.roo-input-before',true).first());
8922 if (typeof(this.after) == 'object') {
8923 this.after.render(this.el.select('.roo-input-after',true).first());
8928 filterValidation : function(e){
8929 if(!e.isNavKeyPress()){
8930 this.validationTask.delay(this.validationDelay);
8934 * Validates the field value
8935 * @return {Boolean} True if the value is valid, else false
8937 validate : function(){
8938 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8939 if(this.disabled || this.validateValue(this.getRawValue())){
8950 * Validates a value according to the field's validation rules and marks the field as invalid
8951 * if the validation fails
8952 * @param {Mixed} value The value to validate
8953 * @return {Boolean} True if the value is valid, else false
8955 validateValue : function(value){
8956 if(value.length < 1) { // if it's blank
8957 if(this.allowBlank){
8960 return this.inputEl().hasClass('hide') ? true : false;
8963 if(value.length < this.minLength){
8966 if(value.length > this.maxLength){
8970 var vt = Roo.form.VTypes;
8971 if(!vt[this.vtype](value, this)){
8975 if(typeof this.validator == "function"){
8976 var msg = this.validator(value);
8980 if (typeof(msg) == 'string') {
8981 this.invalidText = msg;
8985 if(this.regex && !this.regex.test(value)){
8995 fireKey : function(e){
8996 //Roo.log('field ' + e.getKey());
8997 if(e.isNavKeyPress()){
8998 this.fireEvent("specialkey", this, e);
9001 focus : function (selectText){
9003 this.inputEl().focus();
9004 if(selectText === true){
9005 this.inputEl().dom.select();
9011 onFocus : function(){
9012 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9013 // this.el.addClass(this.focusClass);
9016 this.hasFocus = true;
9017 this.startValue = this.getValue();
9018 this.fireEvent("focus", this);
9022 beforeBlur : Roo.emptyFn,
9026 onBlur : function(){
9028 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9029 //this.el.removeClass(this.focusClass);
9031 this.hasFocus = false;
9032 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9035 var v = this.getValue();
9036 if(String(v) !== String(this.startValue)){
9037 this.fireEvent('change', this, v, this.startValue);
9039 this.fireEvent("blur", this);
9043 * Resets the current field value to the originally loaded value and clears any validation messages
9046 this.setValue(this.originalValue);
9050 * Returns the name of the field
9051 * @return {Mixed} name The name field
9053 getName: function(){
9057 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
9058 * @return {Mixed} value The field value
9060 getValue : function(){
9062 var v = this.inputEl().getValue();
9067 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
9068 * @return {Mixed} value The field value
9070 getRawValue : function(){
9071 var v = this.inputEl().getValue();
9077 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
9078 * @param {Mixed} value The value to set
9080 setRawValue : function(v){
9081 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9084 selectText : function(start, end){
9085 var v = this.getRawValue();
9087 start = start === undefined ? 0 : start;
9088 end = end === undefined ? v.length : end;
9089 var d = this.inputEl().dom;
9090 if(d.setSelectionRange){
9091 d.setSelectionRange(start, end);
9092 }else if(d.createTextRange){
9093 var range = d.createTextRange();
9094 range.moveStart("character", start);
9095 range.moveEnd("character", v.length-end);
9102 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
9103 * @param {Mixed} value The value to set
9105 setValue : function(v){
9108 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9114 processValue : function(value){
9115 if(this.stripCharsRe){
9116 var newValue = value.replace(this.stripCharsRe, '');
9117 if(newValue !== value){
9118 this.setRawValue(newValue);
9125 preFocus : function(){
9127 if(this.selectOnFocus){
9128 this.inputEl().dom.select();
9131 filterKeys : function(e){
9133 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9136 var c = e.getCharCode(), cc = String.fromCharCode(c);
9137 if(Roo.isIE && (e.isSpecialKey() || !cc)){
9140 if(!this.maskRe.test(cc)){
9145 * Clear any invalid styles/messages for this field
9147 clearInvalid : function(){
9149 if(!this.el || this.preventMark){ // not rendered
9154 this.el.removeClass(this.invalidClass);
9156 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9158 var feedback = this.el.select('.form-control-feedback', true).first();
9161 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9166 this.fireEvent('valid', this);
9170 * Mark this field as valid
9172 markValid : function()
9174 if(!this.el || this.preventMark){ // not rendered...
9178 this.el.removeClass([this.invalidClass, this.validClass]);
9180 var feedback = this.el.select('.form-control-feedback', true).first();
9183 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9190 if(this.allowBlank && !this.getRawValue().length){
9195 this.indicator.hide();
9198 this.el.addClass(this.validClass);
9200 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9202 var feedback = this.el.select('.form-control-feedback', true).first();
9205 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9206 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9211 this.fireEvent('valid', this);
9215 * Mark this field as invalid
9216 * @param {String} msg The validation message
9218 markInvalid : function(msg)
9220 if(!this.el || this.preventMark){ // not rendered
9224 this.el.removeClass([this.invalidClass, this.validClass]);
9226 var feedback = this.el.select('.form-control-feedback', true).first();
9229 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9236 if(this.allowBlank && !this.getRawValue().length){
9241 this.indicator.show();
9244 this.el.addClass(this.invalidClass);
9246 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9248 var feedback = this.el.select('.form-control-feedback', true).first();
9251 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9253 if(this.getValue().length || this.forceFeedback){
9254 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9261 this.fireEvent('invalid', this, msg);
9264 SafariOnKeyDown : function(event)
9266 // this is a workaround for a password hang bug on chrome/ webkit.
9267 if (this.inputEl().dom.type != 'password') {
9271 var isSelectAll = false;
9273 if(this.inputEl().dom.selectionEnd > 0){
9274 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9276 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9277 event.preventDefault();
9282 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9284 event.preventDefault();
9285 // this is very hacky as keydown always get's upper case.
9287 var cc = String.fromCharCode(event.getCharCode());
9288 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
9292 adjustWidth : function(tag, w){
9293 tag = tag.toLowerCase();
9294 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9295 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9299 if(tag == 'textarea'){
9302 }else if(Roo.isOpera){
9306 if(tag == 'textarea'){
9325 * @class Roo.bootstrap.TextArea
9326 * @extends Roo.bootstrap.Input
9327 * Bootstrap TextArea class
9328 * @cfg {Number} cols Specifies the visible width of a text area
9329 * @cfg {Number} rows Specifies the visible number of lines in a text area
9330 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9331 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9332 * @cfg {string} html text
9335 * Create a new TextArea
9336 * @param {Object} config The config object
9339 Roo.bootstrap.TextArea = function(config){
9340 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9344 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
9354 getAutoCreate : function(){
9356 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9367 value : this.value || '',
9368 html: this.html || '',
9369 cls : 'form-control',
9370 placeholder : this.placeholder || ''
9374 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9375 input.maxLength = this.maxLength;
9379 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9383 input.cols = this.cols;
9386 if (this.readOnly) {
9387 input.readonly = true;
9391 input.name = this.name;
9395 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9399 ['xs','sm','md','lg'].map(function(size){
9400 if (settings[size]) {
9401 cfg.cls += ' col-' + size + '-' + settings[size];
9405 var inputblock = input;
9407 if(this.hasFeedback && !this.allowBlank){
9411 cls: 'glyphicon form-control-feedback'
9415 cls : 'has-feedback',
9424 if (this.before || this.after) {
9427 cls : 'input-group',
9431 inputblock.cn.push({
9433 cls : 'input-group-addon',
9438 inputblock.cn.push(input);
9440 if(this.hasFeedback && !this.allowBlank){
9441 inputblock.cls += ' has-feedback';
9442 inputblock.cn.push(feedback);
9446 inputblock.cn.push({
9448 cls : 'input-group-addon',
9455 if (align ==='left' && this.fieldLabel.length) {
9460 cls : 'control-label',
9461 html : this.fieldLabel
9472 if(this.labelWidth > 12){
9473 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9476 if(this.labelWidth < 13 && this.labelmd == 0){
9477 this.labelmd = this.labelWidth;
9480 if(this.labellg > 0){
9481 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9482 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9485 if(this.labelmd > 0){
9486 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9487 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9490 if(this.labelsm > 0){
9491 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9492 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9495 if(this.labelxs > 0){
9496 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9497 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9500 } else if ( this.fieldLabel.length) {
9505 //cls : 'input-group-addon',
9506 html : this.fieldLabel
9524 if (this.disabled) {
9525 input.disabled=true;
9532 * return the real textarea element.
9534 inputEl: function ()
9536 return this.el.select('textarea.form-control',true).first();
9540 * Clear any invalid styles/messages for this field
9542 clearInvalid : function()
9545 if(!this.el || this.preventMark){ // not rendered
9549 var label = this.el.select('label', true).first();
9550 var icon = this.el.select('i.fa-star', true).first();
9556 this.el.removeClass(this.invalidClass);
9558 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9560 var feedback = this.el.select('.form-control-feedback', true).first();
9563 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9568 this.fireEvent('valid', this);
9572 * Mark this field as valid
9574 markValid : function()
9576 if(!this.el || this.preventMark){ // not rendered
9580 this.el.removeClass([this.invalidClass, this.validClass]);
9582 var feedback = this.el.select('.form-control-feedback', true).first();
9585 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9588 if(this.disabled || this.allowBlank){
9592 var label = this.el.select('label', true).first();
9593 var icon = this.el.select('i.fa-star', true).first();
9599 this.el.addClass(this.validClass);
9601 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9603 var feedback = this.el.select('.form-control-feedback', true).first();
9606 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9607 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9612 this.fireEvent('valid', this);
9616 * Mark this field as invalid
9617 * @param {String} msg The validation message
9619 markInvalid : function(msg)
9621 if(!this.el || this.preventMark){ // not rendered
9625 this.el.removeClass([this.invalidClass, this.validClass]);
9627 var feedback = this.el.select('.form-control-feedback', true).first();
9630 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9633 if(this.disabled || this.allowBlank){
9637 var label = this.el.select('label', true).first();
9638 var icon = this.el.select('i.fa-star', true).first();
9640 if(!this.getValue().length && label && !icon){
9641 this.el.createChild({
9643 cls : 'text-danger fa fa-lg fa-star',
9644 tooltip : 'This field is required',
9645 style : 'margin-right:5px;'
9649 this.el.addClass(this.invalidClass);
9651 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9653 var feedback = this.el.select('.form-control-feedback', true).first();
9656 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9658 if(this.getValue().length || this.forceFeedback){
9659 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9666 this.fireEvent('invalid', this, msg);
9674 * trigger field - base class for combo..
9679 * @class Roo.bootstrap.TriggerField
9680 * @extends Roo.bootstrap.Input
9681 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9682 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9683 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9684 * for which you can provide a custom implementation. For example:
9686 var trigger = new Roo.bootstrap.TriggerField();
9687 trigger.onTriggerClick = myTriggerFn;
9688 trigger.applyTo('my-field');
9691 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9692 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9693 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
9694 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9695 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9698 * Create a new TriggerField.
9699 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9700 * to the base TextField)
9702 Roo.bootstrap.TriggerField = function(config){
9703 this.mimicing = false;
9704 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9707 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
9709 * @cfg {String} triggerClass A CSS class to apply to the trigger
9712 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9717 * @cfg {Boolean} removable (true|false) special filter default false
9721 /** @cfg {Boolean} grow @hide */
9722 /** @cfg {Number} growMin @hide */
9723 /** @cfg {Number} growMax @hide */
9729 autoSize: Roo.emptyFn,
9736 actionMode : 'wrap',
9741 getAutoCreate : function(){
9743 var align = this.labelAlign || this.parentLabelAlign();
9748 cls: 'form-group' //input-group
9755 type : this.inputType,
9756 cls : 'form-control',
9757 autocomplete: 'new-password',
9758 placeholder : this.placeholder || ''
9762 input.name = this.name;
9765 input.cls += ' input-' + this.size;
9768 if (this.disabled) {
9769 input.disabled=true;
9772 var inputblock = input;
9774 if(this.hasFeedback && !this.allowBlank){
9778 cls: 'glyphicon form-control-feedback'
9781 if(this.removable && !this.editable && !this.tickable){
9783 cls : 'has-feedback',
9789 cls : 'roo-combo-removable-btn close'
9796 cls : 'has-feedback',
9805 if(this.removable && !this.editable && !this.tickable){
9807 cls : 'roo-removable',
9813 cls : 'roo-combo-removable-btn close'
9820 if (this.before || this.after) {
9823 cls : 'input-group',
9827 inputblock.cn.push({
9829 cls : 'input-group-addon',
9834 inputblock.cn.push(input);
9836 if(this.hasFeedback && !this.allowBlank){
9837 inputblock.cls += ' has-feedback';
9838 inputblock.cn.push(feedback);
9842 inputblock.cn.push({
9844 cls : 'input-group-addon',
9857 cls: 'form-hidden-field'
9871 cls: 'form-hidden-field'
9875 cls: 'roo-select2-choices',
9879 cls: 'roo-select2-search-field',
9892 cls: 'roo-select2-container input-group',
9897 // cls: 'typeahead typeahead-long dropdown-menu',
9898 // style: 'display:none'
9903 if(!this.multiple && this.showToggleBtn){
9909 if (this.caret != false) {
9912 cls: 'fa fa-' + this.caret
9919 cls : 'input-group-addon btn dropdown-toggle',
9924 cls: 'combobox-clear',
9938 combobox.cls += ' roo-select2-container-multi';
9941 if (align ==='left' && this.fieldLabel.length) {
9943 cfg.cls += ' roo-form-group-label-left';
9948 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9949 tooltip : 'This field is required'
9954 cls : 'control-label',
9955 html : this.fieldLabel
9967 var labelCfg = cfg.cn[1];
9968 var contentCfg = cfg.cn[2];
9970 if(this.indicatorpos == 'right'){
9975 cls : 'control-label',
9979 html : this.fieldLabel
9983 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9984 tooltip : 'This field is required'
9997 labelCfg = cfg.cn[0];
9998 contentCfg = cfg.cn[1];
10001 if(this.labelWidth > 12){
10002 labelCfg.style = "width: " + this.labelWidth + 'px';
10005 if(this.labelWidth < 13 && this.labelmd == 0){
10006 this.labelmd = this.labelWidth;
10009 if(this.labellg > 0){
10010 labelCfg.cls += ' col-lg-' + this.labellg;
10011 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10014 if(this.labelmd > 0){
10015 labelCfg.cls += ' col-md-' + this.labelmd;
10016 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10019 if(this.labelsm > 0){
10020 labelCfg.cls += ' col-sm-' + this.labelsm;
10021 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10024 if(this.labelxs > 0){
10025 labelCfg.cls += ' col-xs-' + this.labelxs;
10026 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10029 } else if ( this.fieldLabel.length) {
10030 // Roo.log(" label");
10034 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10035 tooltip : 'This field is required'
10039 //cls : 'input-group-addon',
10040 html : this.fieldLabel
10048 if(this.indicatorpos == 'right'){
10056 html : this.fieldLabel
10060 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10061 tooltip : 'This field is required'
10074 // Roo.log(" no label && no align");
10081 ['xs','sm','md','lg'].map(function(size){
10082 if (settings[size]) {
10083 cfg.cls += ' col-' + size + '-' + settings[size];
10094 onResize : function(w, h){
10095 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10096 // if(typeof w == 'number'){
10097 // var x = w - this.trigger.getWidth();
10098 // this.inputEl().setWidth(this.adjustWidth('input', x));
10099 // this.trigger.setStyle('left', x+'px');
10104 adjustSize : Roo.BoxComponent.prototype.adjustSize,
10107 getResizeEl : function(){
10108 return this.inputEl();
10112 getPositionEl : function(){
10113 return this.inputEl();
10117 alignErrorIcon : function(){
10118 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10122 initEvents : function(){
10126 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10127 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10128 if(!this.multiple && this.showToggleBtn){
10129 this.trigger = this.el.select('span.dropdown-toggle',true).first();
10130 if(this.hideTrigger){
10131 this.trigger.setDisplayed(false);
10133 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10137 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10140 if(this.removable && !this.editable && !this.tickable){
10141 var close = this.closeTriggerEl();
10144 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10145 close.on('click', this.removeBtnClick, this, close);
10149 //this.trigger.addClassOnOver('x-form-trigger-over');
10150 //this.trigger.addClassOnClick('x-form-trigger-click');
10153 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10157 closeTriggerEl : function()
10159 var close = this.el.select('.roo-combo-removable-btn', true).first();
10160 return close ? close : false;
10163 removeBtnClick : function(e, h, el)
10165 e.preventDefault();
10167 if(this.fireEvent("remove", this) !== false){
10169 this.fireEvent("afterremove", this)
10173 createList : function()
10175 this.list = Roo.get(document.body).createChild({
10177 cls: 'typeahead typeahead-long dropdown-menu',
10178 style: 'display:none'
10181 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10186 initTrigger : function(){
10191 onDestroy : function(){
10193 this.trigger.removeAllListeners();
10194 // this.trigger.remove();
10197 // this.wrap.remove();
10199 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10203 onFocus : function(){
10204 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10206 if(!this.mimicing){
10207 this.wrap.addClass('x-trigger-wrap-focus');
10208 this.mimicing = true;
10209 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10210 if(this.monitorTab){
10211 this.el.on("keydown", this.checkTab, this);
10218 checkTab : function(e){
10219 if(e.getKey() == e.TAB){
10220 this.triggerBlur();
10225 onBlur : function(){
10230 mimicBlur : function(e, t){
10232 if(!this.wrap.contains(t) && this.validateBlur()){
10233 this.triggerBlur();
10239 triggerBlur : function(){
10240 this.mimicing = false;
10241 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10242 if(this.monitorTab){
10243 this.el.un("keydown", this.checkTab, this);
10245 //this.wrap.removeClass('x-trigger-wrap-focus');
10246 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10250 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10251 validateBlur : function(e, t){
10256 onDisable : function(){
10257 this.inputEl().dom.disabled = true;
10258 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10260 // this.wrap.addClass('x-item-disabled');
10265 onEnable : function(){
10266 this.inputEl().dom.disabled = false;
10267 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10269 // this.el.removeClass('x-item-disabled');
10274 onShow : function(){
10275 var ae = this.getActionEl();
10278 ae.dom.style.display = '';
10279 ae.dom.style.visibility = 'visible';
10285 onHide : function(){
10286 var ae = this.getActionEl();
10287 ae.dom.style.display = 'none';
10291 * The function that should handle the trigger's click event. This method does nothing by default until overridden
10292 * by an implementing function.
10294 * @param {EventObject} e
10296 onTriggerClick : Roo.emptyFn
10300 * Ext JS Library 1.1.1
10301 * Copyright(c) 2006-2007, Ext JS, LLC.
10303 * Originally Released Under LGPL - original licence link has changed is not relivant.
10306 * <script type="text/javascript">
10311 * @class Roo.data.SortTypes
10313 * Defines the default sorting (casting?) comparison functions used when sorting data.
10315 Roo.data.SortTypes = {
10317 * Default sort that does nothing
10318 * @param {Mixed} s The value being converted
10319 * @return {Mixed} The comparison value
10321 none : function(s){
10326 * The regular expression used to strip tags
10330 stripTagsRE : /<\/?[^>]+>/gi,
10333 * Strips all HTML tags to sort on text only
10334 * @param {Mixed} s The value being converted
10335 * @return {String} The comparison value
10337 asText : function(s){
10338 return String(s).replace(this.stripTagsRE, "");
10342 * Strips all HTML tags to sort on text only - Case insensitive
10343 * @param {Mixed} s The value being converted
10344 * @return {String} The comparison value
10346 asUCText : function(s){
10347 return String(s).toUpperCase().replace(this.stripTagsRE, "");
10351 * Case insensitive string
10352 * @param {Mixed} s The value being converted
10353 * @return {String} The comparison value
10355 asUCString : function(s) {
10356 return String(s).toUpperCase();
10361 * @param {Mixed} s The value being converted
10362 * @return {Number} The comparison value
10364 asDate : function(s) {
10368 if(s instanceof Date){
10369 return s.getTime();
10371 return Date.parse(String(s));
10376 * @param {Mixed} s The value being converted
10377 * @return {Float} The comparison value
10379 asFloat : function(s) {
10380 var val = parseFloat(String(s).replace(/,/g, ""));
10389 * @param {Mixed} s The value being converted
10390 * @return {Number} The comparison value
10392 asInt : function(s) {
10393 var val = parseInt(String(s).replace(/,/g, ""));
10401 * Ext JS Library 1.1.1
10402 * Copyright(c) 2006-2007, Ext JS, LLC.
10404 * Originally Released Under LGPL - original licence link has changed is not relivant.
10407 * <script type="text/javascript">
10411 * @class Roo.data.Record
10412 * Instances of this class encapsulate both record <em>definition</em> information, and record
10413 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10414 * to access Records cached in an {@link Roo.data.Store} object.<br>
10416 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10417 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10420 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10422 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10423 * {@link #create}. The parameters are the same.
10424 * @param {Array} data An associative Array of data values keyed by the field name.
10425 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10426 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10427 * not specified an integer id is generated.
10429 Roo.data.Record = function(data, id){
10430 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10435 * Generate a constructor for a specific record layout.
10436 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10437 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10438 * Each field definition object may contain the following properties: <ul>
10439 * <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,
10440 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10441 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10442 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10443 * is being used, then this is a string containing the javascript expression to reference the data relative to
10444 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10445 * to the data item relative to the record element. If the mapping expression is the same as the field name,
10446 * this may be omitted.</p></li>
10447 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10448 * <ul><li>auto (Default, implies no conversion)</li>
10453 * <li>date</li></ul></p></li>
10454 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10455 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10456 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10457 * by the Reader into an object that will be stored in the Record. It is passed the
10458 * following parameters:<ul>
10459 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10461 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10463 * <br>usage:<br><pre><code>
10464 var TopicRecord = Roo.data.Record.create(
10465 {name: 'title', mapping: 'topic_title'},
10466 {name: 'author', mapping: 'username'},
10467 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10468 {name: 'lastPost', mapping: 'post_time', type: 'date'},
10469 {name: 'lastPoster', mapping: 'user2'},
10470 {name: 'excerpt', mapping: 'post_text'}
10473 var myNewRecord = new TopicRecord({
10474 title: 'Do my job please',
10477 lastPost: new Date(),
10478 lastPoster: 'Animal',
10479 excerpt: 'No way dude!'
10481 myStore.add(myNewRecord);
10486 Roo.data.Record.create = function(o){
10487 var f = function(){
10488 f.superclass.constructor.apply(this, arguments);
10490 Roo.extend(f, Roo.data.Record);
10491 var p = f.prototype;
10492 p.fields = new Roo.util.MixedCollection(false, function(field){
10495 for(var i = 0, len = o.length; i < len; i++){
10496 p.fields.add(new Roo.data.Field(o[i]));
10498 f.getField = function(name){
10499 return p.fields.get(name);
10504 Roo.data.Record.AUTO_ID = 1000;
10505 Roo.data.Record.EDIT = 'edit';
10506 Roo.data.Record.REJECT = 'reject';
10507 Roo.data.Record.COMMIT = 'commit';
10509 Roo.data.Record.prototype = {
10511 * Readonly flag - true if this record has been modified.
10520 join : function(store){
10521 this.store = store;
10525 * Set the named field to the specified value.
10526 * @param {String} name The name of the field to set.
10527 * @param {Object} value The value to set the field to.
10529 set : function(name, value){
10530 if(this.data[name] == value){
10534 if(!this.modified){
10535 this.modified = {};
10537 if(typeof this.modified[name] == 'undefined'){
10538 this.modified[name] = this.data[name];
10540 this.data[name] = value;
10541 if(!this.editing && this.store){
10542 this.store.afterEdit(this);
10547 * Get the value of the named field.
10548 * @param {String} name The name of the field to get the value of.
10549 * @return {Object} The value of the field.
10551 get : function(name){
10552 return this.data[name];
10556 beginEdit : function(){
10557 this.editing = true;
10558 this.modified = {};
10562 cancelEdit : function(){
10563 this.editing = false;
10564 delete this.modified;
10568 endEdit : function(){
10569 this.editing = false;
10570 if(this.dirty && this.store){
10571 this.store.afterEdit(this);
10576 * Usually called by the {@link Roo.data.Store} which owns the Record.
10577 * Rejects all changes made to the Record since either creation, or the last commit operation.
10578 * Modified fields are reverted to their original values.
10580 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10581 * of reject operations.
10583 reject : function(){
10584 var m = this.modified;
10586 if(typeof m[n] != "function"){
10587 this.data[n] = m[n];
10590 this.dirty = false;
10591 delete this.modified;
10592 this.editing = false;
10594 this.store.afterReject(this);
10599 * Usually called by the {@link Roo.data.Store} which owns the Record.
10600 * Commits all changes made to the Record since either creation, or the last commit operation.
10602 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10603 * of commit operations.
10605 commit : function(){
10606 this.dirty = false;
10607 delete this.modified;
10608 this.editing = false;
10610 this.store.afterCommit(this);
10615 hasError : function(){
10616 return this.error != null;
10620 clearError : function(){
10625 * Creates a copy of this record.
10626 * @param {String} id (optional) A new record id if you don't want to use this record's id
10629 copy : function(newId) {
10630 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10634 * Ext JS Library 1.1.1
10635 * Copyright(c) 2006-2007, Ext JS, LLC.
10637 * Originally Released Under LGPL - original licence link has changed is not relivant.
10640 * <script type="text/javascript">
10646 * @class Roo.data.Store
10647 * @extends Roo.util.Observable
10648 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10649 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10651 * 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
10652 * has no knowledge of the format of the data returned by the Proxy.<br>
10654 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10655 * instances from the data object. These records are cached and made available through accessor functions.
10657 * Creates a new Store.
10658 * @param {Object} config A config object containing the objects needed for the Store to access data,
10659 * and read the data into Records.
10661 Roo.data.Store = function(config){
10662 this.data = new Roo.util.MixedCollection(false);
10663 this.data.getKey = function(o){
10666 this.baseParams = {};
10668 this.paramNames = {
10673 "multisort" : "_multisort"
10676 if(config && config.data){
10677 this.inlineData = config.data;
10678 delete config.data;
10681 Roo.apply(this, config);
10683 if(this.reader){ // reader passed
10684 this.reader = Roo.factory(this.reader, Roo.data);
10685 this.reader.xmodule = this.xmodule || false;
10686 if(!this.recordType){
10687 this.recordType = this.reader.recordType;
10689 if(this.reader.onMetaChange){
10690 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10694 if(this.recordType){
10695 this.fields = this.recordType.prototype.fields;
10697 this.modified = [];
10701 * @event datachanged
10702 * Fires when the data cache has changed, and a widget which is using this Store
10703 * as a Record cache should refresh its view.
10704 * @param {Store} this
10706 datachanged : true,
10708 * @event metachange
10709 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10710 * @param {Store} this
10711 * @param {Object} meta The JSON metadata
10716 * Fires when Records have been added to the Store
10717 * @param {Store} this
10718 * @param {Roo.data.Record[]} records The array of Records added
10719 * @param {Number} index The index at which the record(s) were added
10724 * Fires when a Record has been removed from the Store
10725 * @param {Store} this
10726 * @param {Roo.data.Record} record The Record that was removed
10727 * @param {Number} index The index at which the record was removed
10732 * Fires when a Record has been updated
10733 * @param {Store} this
10734 * @param {Roo.data.Record} record The Record that was updated
10735 * @param {String} operation The update operation being performed. Value may be one of:
10737 Roo.data.Record.EDIT
10738 Roo.data.Record.REJECT
10739 Roo.data.Record.COMMIT
10745 * Fires when the data cache has been cleared.
10746 * @param {Store} this
10750 * @event beforeload
10751 * Fires before a request is made for a new data object. If the beforeload handler returns false
10752 * the load action will be canceled.
10753 * @param {Store} this
10754 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10758 * @event beforeloadadd
10759 * Fires after a new set of Records has been loaded.
10760 * @param {Store} this
10761 * @param {Roo.data.Record[]} records The Records that were loaded
10762 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10764 beforeloadadd : true,
10767 * Fires after a new set of Records has been loaded, before they are added to the store.
10768 * @param {Store} this
10769 * @param {Roo.data.Record[]} records The Records that were loaded
10770 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10771 * @params {Object} return from reader
10775 * @event loadexception
10776 * Fires if an exception occurs in the Proxy during loading.
10777 * Called with the signature of the Proxy's "loadexception" event.
10778 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10781 * @param {Object} return from JsonData.reader() - success, totalRecords, records
10782 * @param {Object} load options
10783 * @param {Object} jsonData from your request (normally this contains the Exception)
10785 loadexception : true
10789 this.proxy = Roo.factory(this.proxy, Roo.data);
10790 this.proxy.xmodule = this.xmodule || false;
10791 this.relayEvents(this.proxy, ["loadexception"]);
10793 this.sortToggle = {};
10794 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10796 Roo.data.Store.superclass.constructor.call(this);
10798 if(this.inlineData){
10799 this.loadData(this.inlineData);
10800 delete this.inlineData;
10804 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10806 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
10807 * without a remote query - used by combo/forms at present.
10811 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10814 * @cfg {Array} data Inline data to be loaded when the store is initialized.
10817 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10818 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10821 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10822 * on any HTTP request
10825 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10828 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10832 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10833 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10835 remoteSort : false,
10838 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10839 * loaded or when a record is removed. (defaults to false).
10841 pruneModifiedRecords : false,
10844 lastOptions : null,
10847 * Add Records to the Store and fires the add event.
10848 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10850 add : function(records){
10851 records = [].concat(records);
10852 for(var i = 0, len = records.length; i < len; i++){
10853 records[i].join(this);
10855 var index = this.data.length;
10856 this.data.addAll(records);
10857 this.fireEvent("add", this, records, index);
10861 * Remove a Record from the Store and fires the remove event.
10862 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10864 remove : function(record){
10865 var index = this.data.indexOf(record);
10866 this.data.removeAt(index);
10867 if(this.pruneModifiedRecords){
10868 this.modified.remove(record);
10870 this.fireEvent("remove", this, record, index);
10874 * Remove all Records from the Store and fires the clear event.
10876 removeAll : function(){
10878 if(this.pruneModifiedRecords){
10879 this.modified = [];
10881 this.fireEvent("clear", this);
10885 * Inserts Records to the Store at the given index and fires the add event.
10886 * @param {Number} index The start index at which to insert the passed Records.
10887 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10889 insert : function(index, records){
10890 records = [].concat(records);
10891 for(var i = 0, len = records.length; i < len; i++){
10892 this.data.insert(index, records[i]);
10893 records[i].join(this);
10895 this.fireEvent("add", this, records, index);
10899 * Get the index within the cache of the passed Record.
10900 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10901 * @return {Number} The index of the passed Record. Returns -1 if not found.
10903 indexOf : function(record){
10904 return this.data.indexOf(record);
10908 * Get the index within the cache of the Record with the passed id.
10909 * @param {String} id The id of the Record to find.
10910 * @return {Number} The index of the Record. Returns -1 if not found.
10912 indexOfId : function(id){
10913 return this.data.indexOfKey(id);
10917 * Get the Record with the specified id.
10918 * @param {String} id The id of the Record to find.
10919 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10921 getById : function(id){
10922 return this.data.key(id);
10926 * Get the Record at the specified index.
10927 * @param {Number} index The index of the Record to find.
10928 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10930 getAt : function(index){
10931 return this.data.itemAt(index);
10935 * Returns a range of Records between specified indices.
10936 * @param {Number} startIndex (optional) The starting index (defaults to 0)
10937 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10938 * @return {Roo.data.Record[]} An array of Records
10940 getRange : function(start, end){
10941 return this.data.getRange(start, end);
10945 storeOptions : function(o){
10946 o = Roo.apply({}, o);
10949 this.lastOptions = o;
10953 * Loads the Record cache from the configured Proxy using the configured Reader.
10955 * If using remote paging, then the first load call must specify the <em>start</em>
10956 * and <em>limit</em> properties in the options.params property to establish the initial
10957 * position within the dataset, and the number of Records to cache on each read from the Proxy.
10959 * <strong>It is important to note that for remote data sources, loading is asynchronous,
10960 * and this call will return before the new data has been loaded. Perform any post-processing
10961 * in a callback function, or in a "load" event handler.</strong>
10963 * @param {Object} options An object containing properties which control loading options:<ul>
10964 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10965 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10966 * passed the following arguments:<ul>
10967 * <li>r : Roo.data.Record[]</li>
10968 * <li>options: Options object from the load call</li>
10969 * <li>success: Boolean success indicator</li></ul></li>
10970 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10971 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10974 load : function(options){
10975 options = options || {};
10976 if(this.fireEvent("beforeload", this, options) !== false){
10977 this.storeOptions(options);
10978 var p = Roo.apply(options.params || {}, this.baseParams);
10979 // if meta was not loaded from remote source.. try requesting it.
10980 if (!this.reader.metaFromRemote) {
10981 p._requestMeta = 1;
10983 if(this.sortInfo && this.remoteSort){
10984 var pn = this.paramNames;
10985 p[pn["sort"]] = this.sortInfo.field;
10986 p[pn["dir"]] = this.sortInfo.direction;
10988 if (this.multiSort) {
10989 var pn = this.paramNames;
10990 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10993 this.proxy.load(p, this.reader, this.loadRecords, this, options);
10998 * Reloads the Record cache from the configured Proxy using the configured Reader and
10999 * the options from the last load operation performed.
11000 * @param {Object} options (optional) An object containing properties which may override the options
11001 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11002 * the most recently used options are reused).
11004 reload : function(options){
11005 this.load(Roo.applyIf(options||{}, this.lastOptions));
11009 // Called as a callback by the Reader during a load operation.
11010 loadRecords : function(o, options, success){
11011 if(!o || success === false){
11012 if(success !== false){
11013 this.fireEvent("load", this, [], options, o);
11015 if(options.callback){
11016 options.callback.call(options.scope || this, [], options, false);
11020 // if data returned failure - throw an exception.
11021 if (o.success === false) {
11022 // show a message if no listener is registered.
11023 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11024 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11026 // loadmask wil be hooked into this..
11027 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11030 var r = o.records, t = o.totalRecords || r.length;
11032 this.fireEvent("beforeloadadd", this, r, options, o);
11034 if(!options || options.add !== true){
11035 if(this.pruneModifiedRecords){
11036 this.modified = [];
11038 for(var i = 0, len = r.length; i < len; i++){
11042 this.data = this.snapshot;
11043 delete this.snapshot;
11046 this.data.addAll(r);
11047 this.totalLength = t;
11049 this.fireEvent("datachanged", this);
11051 this.totalLength = Math.max(t, this.data.length+r.length);
11055 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11057 var e = new Roo.data.Record({});
11059 e.set(this.parent.displayField, this.parent.emptyTitle);
11060 e.set(this.parent.valueField, '');
11065 this.fireEvent("load", this, r, options, o);
11066 if(options.callback){
11067 options.callback.call(options.scope || this, r, options, true);
11073 * Loads data from a passed data block. A Reader which understands the format of the data
11074 * must have been configured in the constructor.
11075 * @param {Object} data The data block from which to read the Records. The format of the data expected
11076 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11077 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11079 loadData : function(o, append){
11080 var r = this.reader.readRecords(o);
11081 this.loadRecords(r, {add: append}, true);
11085 * Gets the number of cached records.
11087 * <em>If using paging, this may not be the total size of the dataset. If the data object
11088 * used by the Reader contains the dataset size, then the getTotalCount() function returns
11089 * the data set size</em>
11091 getCount : function(){
11092 return this.data.length || 0;
11096 * Gets the total number of records in the dataset as returned by the server.
11098 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11099 * the dataset size</em>
11101 getTotalCount : function(){
11102 return this.totalLength || 0;
11106 * Returns the sort state of the Store as an object with two properties:
11108 field {String} The name of the field by which the Records are sorted
11109 direction {String} The sort order, "ASC" or "DESC"
11112 getSortState : function(){
11113 return this.sortInfo;
11117 applySort : function(){
11118 if(this.sortInfo && !this.remoteSort){
11119 var s = this.sortInfo, f = s.field;
11120 var st = this.fields.get(f).sortType;
11121 var fn = function(r1, r2){
11122 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11123 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11125 this.data.sort(s.direction, fn);
11126 if(this.snapshot && this.snapshot != this.data){
11127 this.snapshot.sort(s.direction, fn);
11133 * Sets the default sort column and order to be used by the next load operation.
11134 * @param {String} fieldName The name of the field to sort by.
11135 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11137 setDefaultSort : function(field, dir){
11138 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11142 * Sort the Records.
11143 * If remote sorting is used, the sort is performed on the server, and the cache is
11144 * reloaded. If local sorting is used, the cache is sorted internally.
11145 * @param {String} fieldName The name of the field to sort by.
11146 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11148 sort : function(fieldName, dir){
11149 var f = this.fields.get(fieldName);
11151 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11153 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11154 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11159 this.sortToggle[f.name] = dir;
11160 this.sortInfo = {field: f.name, direction: dir};
11161 if(!this.remoteSort){
11163 this.fireEvent("datachanged", this);
11165 this.load(this.lastOptions);
11170 * Calls the specified function for each of the Records in the cache.
11171 * @param {Function} fn The function to call. The Record is passed as the first parameter.
11172 * Returning <em>false</em> aborts and exits the iteration.
11173 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11175 each : function(fn, scope){
11176 this.data.each(fn, scope);
11180 * Gets all records modified since the last commit. Modified records are persisted across load operations
11181 * (e.g., during paging).
11182 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11184 getModifiedRecords : function(){
11185 return this.modified;
11189 createFilterFn : function(property, value, anyMatch){
11190 if(!value.exec){ // not a regex
11191 value = String(value);
11192 if(value.length == 0){
11195 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11197 return function(r){
11198 return value.test(r.data[property]);
11203 * Sums the value of <i>property</i> for each record between start and end and returns the result.
11204 * @param {String} property A field on your records
11205 * @param {Number} start The record index to start at (defaults to 0)
11206 * @param {Number} end The last record index to include (defaults to length - 1)
11207 * @return {Number} The sum
11209 sum : function(property, start, end){
11210 var rs = this.data.items, v = 0;
11211 start = start || 0;
11212 end = (end || end === 0) ? end : rs.length-1;
11214 for(var i = start; i <= end; i++){
11215 v += (rs[i].data[property] || 0);
11221 * Filter the records by a specified property.
11222 * @param {String} field A field on your records
11223 * @param {String/RegExp} value Either a string that the field
11224 * should start with or a RegExp to test against the field
11225 * @param {Boolean} anyMatch True to match any part not just the beginning
11227 filter : function(property, value, anyMatch){
11228 var fn = this.createFilterFn(property, value, anyMatch);
11229 return fn ? this.filterBy(fn) : this.clearFilter();
11233 * Filter by a function. The specified function will be called with each
11234 * record in this data source. If the function returns true the record is included,
11235 * otherwise it is filtered.
11236 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11237 * @param {Object} scope (optional) The scope of the function (defaults to this)
11239 filterBy : function(fn, scope){
11240 this.snapshot = this.snapshot || this.data;
11241 this.data = this.queryBy(fn, scope||this);
11242 this.fireEvent("datachanged", this);
11246 * Query the records by a specified property.
11247 * @param {String} field A field on your records
11248 * @param {String/RegExp} value Either a string that the field
11249 * should start with or a RegExp to test against the field
11250 * @param {Boolean} anyMatch True to match any part not just the beginning
11251 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11253 query : function(property, value, anyMatch){
11254 var fn = this.createFilterFn(property, value, anyMatch);
11255 return fn ? this.queryBy(fn) : this.data.clone();
11259 * Query by a function. The specified function will be called with each
11260 * record in this data source. If the function returns true the record is included
11262 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11263 * @param {Object} scope (optional) The scope of the function (defaults to this)
11264 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11266 queryBy : function(fn, scope){
11267 var data = this.snapshot || this.data;
11268 return data.filterBy(fn, scope||this);
11272 * Collects unique values for a particular dataIndex from this store.
11273 * @param {String} dataIndex The property to collect
11274 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11275 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11276 * @return {Array} An array of the unique values
11278 collect : function(dataIndex, allowNull, bypassFilter){
11279 var d = (bypassFilter === true && this.snapshot) ?
11280 this.snapshot.items : this.data.items;
11281 var v, sv, r = [], l = {};
11282 for(var i = 0, len = d.length; i < len; i++){
11283 v = d[i].data[dataIndex];
11285 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11294 * Revert to a view of the Record cache with no filtering applied.
11295 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11297 clearFilter : function(suppressEvent){
11298 if(this.snapshot && this.snapshot != this.data){
11299 this.data = this.snapshot;
11300 delete this.snapshot;
11301 if(suppressEvent !== true){
11302 this.fireEvent("datachanged", this);
11308 afterEdit : function(record){
11309 if(this.modified.indexOf(record) == -1){
11310 this.modified.push(record);
11312 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11316 afterReject : function(record){
11317 this.modified.remove(record);
11318 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11322 afterCommit : function(record){
11323 this.modified.remove(record);
11324 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11328 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11329 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11331 commitChanges : function(){
11332 var m = this.modified.slice(0);
11333 this.modified = [];
11334 for(var i = 0, len = m.length; i < len; i++){
11340 * Cancel outstanding changes on all changed records.
11342 rejectChanges : function(){
11343 var m = this.modified.slice(0);
11344 this.modified = [];
11345 for(var i = 0, len = m.length; i < len; i++){
11350 onMetaChange : function(meta, rtype, o){
11351 this.recordType = rtype;
11352 this.fields = rtype.prototype.fields;
11353 delete this.snapshot;
11354 this.sortInfo = meta.sortInfo || this.sortInfo;
11355 this.modified = [];
11356 this.fireEvent('metachange', this, this.reader.meta);
11359 moveIndex : function(data, type)
11361 var index = this.indexOf(data);
11363 var newIndex = index + type;
11367 this.insert(newIndex, data);
11372 * Ext JS Library 1.1.1
11373 * Copyright(c) 2006-2007, Ext JS, LLC.
11375 * Originally Released Under LGPL - original licence link has changed is not relivant.
11378 * <script type="text/javascript">
11382 * @class Roo.data.SimpleStore
11383 * @extends Roo.data.Store
11384 * Small helper class to make creating Stores from Array data easier.
11385 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11386 * @cfg {Array} fields An array of field definition objects, or field name strings.
11387 * @cfg {Array} data The multi-dimensional array of data
11389 * @param {Object} config
11391 Roo.data.SimpleStore = function(config){
11392 Roo.data.SimpleStore.superclass.constructor.call(this, {
11394 reader: new Roo.data.ArrayReader({
11397 Roo.data.Record.create(config.fields)
11399 proxy : new Roo.data.MemoryProxy(config.data)
11403 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11405 * Ext JS Library 1.1.1
11406 * Copyright(c) 2006-2007, Ext JS, LLC.
11408 * Originally Released Under LGPL - original licence link has changed is not relivant.
11411 * <script type="text/javascript">
11416 * @extends Roo.data.Store
11417 * @class Roo.data.JsonStore
11418 * Small helper class to make creating Stores for JSON data easier. <br/>
11420 var store = new Roo.data.JsonStore({
11421 url: 'get-images.php',
11423 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11426 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11427 * JsonReader and HttpProxy (unless inline data is provided).</b>
11428 * @cfg {Array} fields An array of field definition objects, or field name strings.
11430 * @param {Object} config
11432 Roo.data.JsonStore = function(c){
11433 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11434 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11435 reader: new Roo.data.JsonReader(c, c.fields)
11438 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11440 * Ext JS Library 1.1.1
11441 * Copyright(c) 2006-2007, Ext JS, LLC.
11443 * Originally Released Under LGPL - original licence link has changed is not relivant.
11446 * <script type="text/javascript">
11450 Roo.data.Field = function(config){
11451 if(typeof config == "string"){
11452 config = {name: config};
11454 Roo.apply(this, config);
11457 this.type = "auto";
11460 var st = Roo.data.SortTypes;
11461 // named sortTypes are supported, here we look them up
11462 if(typeof this.sortType == "string"){
11463 this.sortType = st[this.sortType];
11466 // set default sortType for strings and dates
11467 if(!this.sortType){
11470 this.sortType = st.asUCString;
11473 this.sortType = st.asDate;
11476 this.sortType = st.none;
11481 var stripRe = /[\$,%]/g;
11483 // prebuilt conversion function for this field, instead of
11484 // switching every time we're reading a value
11486 var cv, dateFormat = this.dateFormat;
11491 cv = function(v){ return v; };
11494 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11498 return v !== undefined && v !== null && v !== '' ?
11499 parseInt(String(v).replace(stripRe, ""), 10) : '';
11504 return v !== undefined && v !== null && v !== '' ?
11505 parseFloat(String(v).replace(stripRe, ""), 10) : '';
11510 cv = function(v){ return v === true || v === "true" || v == 1; };
11517 if(v instanceof Date){
11521 if(dateFormat == "timestamp"){
11522 return new Date(v*1000);
11524 return Date.parseDate(v, dateFormat);
11526 var parsed = Date.parse(v);
11527 return parsed ? new Date(parsed) : null;
11536 Roo.data.Field.prototype = {
11544 * Ext JS Library 1.1.1
11545 * Copyright(c) 2006-2007, Ext JS, LLC.
11547 * Originally Released Under LGPL - original licence link has changed is not relivant.
11550 * <script type="text/javascript">
11553 // Base class for reading structured data from a data source. This class is intended to be
11554 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11557 * @class Roo.data.DataReader
11558 * Base class for reading structured data from a data source. This class is intended to be
11559 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11562 Roo.data.DataReader = function(meta, recordType){
11566 this.recordType = recordType instanceof Array ?
11567 Roo.data.Record.create(recordType) : recordType;
11570 Roo.data.DataReader.prototype = {
11572 * Create an empty record
11573 * @param {Object} data (optional) - overlay some values
11574 * @return {Roo.data.Record} record created.
11576 newRow : function(d) {
11578 this.recordType.prototype.fields.each(function(c) {
11580 case 'int' : da[c.name] = 0; break;
11581 case 'date' : da[c.name] = new Date(); break;
11582 case 'float' : da[c.name] = 0.0; break;
11583 case 'boolean' : da[c.name] = false; break;
11584 default : da[c.name] = ""; break;
11588 return new this.recordType(Roo.apply(da, d));
11593 * Ext JS Library 1.1.1
11594 * Copyright(c) 2006-2007, Ext JS, LLC.
11596 * Originally Released Under LGPL - original licence link has changed is not relivant.
11599 * <script type="text/javascript">
11603 * @class Roo.data.DataProxy
11604 * @extends Roo.data.Observable
11605 * This class is an abstract base class for implementations which provide retrieval of
11606 * unformatted data objects.<br>
11608 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11609 * (of the appropriate type which knows how to parse the data object) to provide a block of
11610 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11612 * Custom implementations must implement the load method as described in
11613 * {@link Roo.data.HttpProxy#load}.
11615 Roo.data.DataProxy = function(){
11618 * @event beforeload
11619 * Fires before a network request is made to retrieve a data object.
11620 * @param {Object} This DataProxy object.
11621 * @param {Object} params The params parameter to the load function.
11626 * Fires before the load method's callback is called.
11627 * @param {Object} This DataProxy object.
11628 * @param {Object} o The data object.
11629 * @param {Object} arg The callback argument object passed to the load function.
11633 * @event loadexception
11634 * Fires if an Exception occurs during data retrieval.
11635 * @param {Object} This DataProxy object.
11636 * @param {Object} o The data object.
11637 * @param {Object} arg The callback argument object passed to the load function.
11638 * @param {Object} e The Exception.
11640 loadexception : true
11642 Roo.data.DataProxy.superclass.constructor.call(this);
11645 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11648 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11652 * Ext JS Library 1.1.1
11653 * Copyright(c) 2006-2007, Ext JS, LLC.
11655 * Originally Released Under LGPL - original licence link has changed is not relivant.
11658 * <script type="text/javascript">
11661 * @class Roo.data.MemoryProxy
11662 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11663 * to the Reader when its load method is called.
11665 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11667 Roo.data.MemoryProxy = function(data){
11671 Roo.data.MemoryProxy.superclass.constructor.call(this);
11675 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11678 * Load data from the requested source (in this case an in-memory
11679 * data object passed to the constructor), read the data object into
11680 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11681 * process that block using the passed callback.
11682 * @param {Object} params This parameter is not used by the MemoryProxy class.
11683 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11684 * object into a block of Roo.data.Records.
11685 * @param {Function} callback The function into which to pass the block of Roo.data.records.
11686 * The function must be passed <ul>
11687 * <li>The Record block object</li>
11688 * <li>The "arg" argument from the load function</li>
11689 * <li>A boolean success indicator</li>
11691 * @param {Object} scope The scope in which to call the callback
11692 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11694 load : function(params, reader, callback, scope, arg){
11695 params = params || {};
11698 result = reader.readRecords(this.data);
11700 this.fireEvent("loadexception", this, arg, null, e);
11701 callback.call(scope, null, arg, false);
11704 callback.call(scope, result, arg, true);
11708 update : function(params, records){
11713 * Ext JS Library 1.1.1
11714 * Copyright(c) 2006-2007, Ext JS, LLC.
11716 * Originally Released Under LGPL - original licence link has changed is not relivant.
11719 * <script type="text/javascript">
11722 * @class Roo.data.HttpProxy
11723 * @extends Roo.data.DataProxy
11724 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11725 * configured to reference a certain URL.<br><br>
11727 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11728 * from which the running page was served.<br><br>
11730 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11732 * Be aware that to enable the browser to parse an XML document, the server must set
11733 * the Content-Type header in the HTTP response to "text/xml".
11735 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11736 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
11737 * will be used to make the request.
11739 Roo.data.HttpProxy = function(conn){
11740 Roo.data.HttpProxy.superclass.constructor.call(this);
11741 // is conn a conn config or a real conn?
11743 this.useAjax = !conn || !conn.events;
11747 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11748 // thse are take from connection...
11751 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11754 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11755 * extra parameters to each request made by this object. (defaults to undefined)
11758 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11759 * to each request made by this object. (defaults to undefined)
11762 * @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)
11765 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11768 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11774 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11778 * Return the {@link Roo.data.Connection} object being used by this Proxy.
11779 * @return {Connection} The Connection object. This object may be used to subscribe to events on
11780 * a finer-grained basis than the DataProxy events.
11782 getConnection : function(){
11783 return this.useAjax ? Roo.Ajax : this.conn;
11787 * Load data from the configured {@link Roo.data.Connection}, read the data object into
11788 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11789 * process that block using the passed callback.
11790 * @param {Object} params An object containing properties which are to be used as HTTP parameters
11791 * for the request to the remote server.
11792 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11793 * object into a block of Roo.data.Records.
11794 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11795 * The function must be passed <ul>
11796 * <li>The Record block object</li>
11797 * <li>The "arg" argument from the load function</li>
11798 * <li>A boolean success indicator</li>
11800 * @param {Object} scope The scope in which to call the callback
11801 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11803 load : function(params, reader, callback, scope, arg){
11804 if(this.fireEvent("beforeload", this, params) !== false){
11806 params : params || {},
11808 callback : callback,
11813 callback : this.loadResponse,
11817 Roo.applyIf(o, this.conn);
11818 if(this.activeRequest){
11819 Roo.Ajax.abort(this.activeRequest);
11821 this.activeRequest = Roo.Ajax.request(o);
11823 this.conn.request(o);
11826 callback.call(scope||this, null, arg, false);
11831 loadResponse : function(o, success, response){
11832 delete this.activeRequest;
11834 this.fireEvent("loadexception", this, o, response);
11835 o.request.callback.call(o.request.scope, null, o.request.arg, false);
11840 result = o.reader.read(response);
11842 this.fireEvent("loadexception", this, o, response, e);
11843 o.request.callback.call(o.request.scope, null, o.request.arg, false);
11847 this.fireEvent("load", this, o, o.request.arg);
11848 o.request.callback.call(o.request.scope, result, o.request.arg, true);
11852 update : function(dataSet){
11857 updateResponse : function(dataSet){
11862 * Ext JS Library 1.1.1
11863 * Copyright(c) 2006-2007, Ext JS, LLC.
11865 * Originally Released Under LGPL - original licence link has changed is not relivant.
11868 * <script type="text/javascript">
11872 * @class Roo.data.ScriptTagProxy
11873 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11874 * other than the originating domain of the running page.<br><br>
11876 * <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
11877 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11879 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11880 * source code that is used as the source inside a <script> tag.<br><br>
11882 * In order for the browser to process the returned data, the server must wrap the data object
11883 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11884 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11885 * depending on whether the callback name was passed:
11888 boolean scriptTag = false;
11889 String cb = request.getParameter("callback");
11892 response.setContentType("text/javascript");
11894 response.setContentType("application/x-json");
11896 Writer out = response.getWriter();
11898 out.write(cb + "(");
11900 out.print(dataBlock.toJsonString());
11907 * @param {Object} config A configuration object.
11909 Roo.data.ScriptTagProxy = function(config){
11910 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11911 Roo.apply(this, config);
11912 this.head = document.getElementsByTagName("head")[0];
11915 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11917 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11919 * @cfg {String} url The URL from which to request the data object.
11922 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11926 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11927 * the server the name of the callback function set up by the load call to process the returned data object.
11928 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11929 * javascript output which calls this named function passing the data object as its only parameter.
11931 callbackParam : "callback",
11933 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11934 * name to the request.
11939 * Load data from the configured URL, read the data object into
11940 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11941 * process that block using the passed callback.
11942 * @param {Object} params An object containing properties which are to be used as HTTP parameters
11943 * for the request to the remote server.
11944 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11945 * object into a block of Roo.data.Records.
11946 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11947 * The function must be passed <ul>
11948 * <li>The Record block object</li>
11949 * <li>The "arg" argument from the load function</li>
11950 * <li>A boolean success indicator</li>
11952 * @param {Object} scope The scope in which to call the callback
11953 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11955 load : function(params, reader, callback, scope, arg){
11956 if(this.fireEvent("beforeload", this, params) !== false){
11958 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11960 var url = this.url;
11961 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11963 url += "&_dc=" + (new Date().getTime());
11965 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11968 cb : "stcCallback"+transId,
11969 scriptId : "stcScript"+transId,
11973 callback : callback,
11979 window[trans.cb] = function(o){
11980 conn.handleResponse(o, trans);
11983 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11985 if(this.autoAbort !== false){
11989 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11991 var script = document.createElement("script");
11992 script.setAttribute("src", url);
11993 script.setAttribute("type", "text/javascript");
11994 script.setAttribute("id", trans.scriptId);
11995 this.head.appendChild(script);
11997 this.trans = trans;
11999 callback.call(scope||this, null, arg, false);
12004 isLoading : function(){
12005 return this.trans ? true : false;
12009 * Abort the current server request.
12011 abort : function(){
12012 if(this.isLoading()){
12013 this.destroyTrans(this.trans);
12018 destroyTrans : function(trans, isLoaded){
12019 this.head.removeChild(document.getElementById(trans.scriptId));
12020 clearTimeout(trans.timeoutId);
12022 window[trans.cb] = undefined;
12024 delete window[trans.cb];
12027 // if hasn't been loaded, wait for load to remove it to prevent script error
12028 window[trans.cb] = function(){
12029 window[trans.cb] = undefined;
12031 delete window[trans.cb];
12038 handleResponse : function(o, trans){
12039 this.trans = false;
12040 this.destroyTrans(trans, true);
12043 result = trans.reader.readRecords(o);
12045 this.fireEvent("loadexception", this, o, trans.arg, e);
12046 trans.callback.call(trans.scope||window, null, trans.arg, false);
12049 this.fireEvent("load", this, o, trans.arg);
12050 trans.callback.call(trans.scope||window, result, trans.arg, true);
12054 handleFailure : function(trans){
12055 this.trans = false;
12056 this.destroyTrans(trans, false);
12057 this.fireEvent("loadexception", this, null, trans.arg);
12058 trans.callback.call(trans.scope||window, null, trans.arg, false);
12062 * Ext JS Library 1.1.1
12063 * Copyright(c) 2006-2007, Ext JS, LLC.
12065 * Originally Released Under LGPL - original licence link has changed is not relivant.
12068 * <script type="text/javascript">
12072 * @class Roo.data.JsonReader
12073 * @extends Roo.data.DataReader
12074 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12075 * based on mappings in a provided Roo.data.Record constructor.
12077 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12078 * in the reply previously.
12083 var RecordDef = Roo.data.Record.create([
12084 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
12085 {name: 'occupation'} // This field will use "occupation" as the mapping.
12087 var myReader = new Roo.data.JsonReader({
12088 totalProperty: "results", // The property which contains the total dataset size (optional)
12089 root: "rows", // The property which contains an Array of row objects
12090 id: "id" // The property within each row object that provides an ID for the record (optional)
12094 * This would consume a JSON file like this:
12096 { 'results': 2, 'rows': [
12097 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12098 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12101 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12102 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12103 * paged from the remote server.
12104 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12105 * @cfg {String} root name of the property which contains the Array of row objects.
12106 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12107 * @cfg {Array} fields Array of field definition objects
12109 * Create a new JsonReader
12110 * @param {Object} meta Metadata configuration options
12111 * @param {Object} recordType Either an Array of field definition objects,
12112 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12114 Roo.data.JsonReader = function(meta, recordType){
12117 // set some defaults:
12118 Roo.applyIf(meta, {
12119 totalProperty: 'total',
12120 successProperty : 'success',
12125 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12127 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12130 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
12131 * Used by Store query builder to append _requestMeta to params.
12134 metaFromRemote : false,
12136 * This method is only used by a DataProxy which has retrieved data from a remote server.
12137 * @param {Object} response The XHR object which contains the JSON data in its responseText.
12138 * @return {Object} data A data block which is used by an Roo.data.Store object as
12139 * a cache of Roo.data.Records.
12141 read : function(response){
12142 var json = response.responseText;
12144 var o = /* eval:var:o */ eval("("+json+")");
12146 throw {message: "JsonReader.read: Json object not found"};
12152 this.metaFromRemote = true;
12153 this.meta = o.metaData;
12154 this.recordType = Roo.data.Record.create(o.metaData.fields);
12155 this.onMetaChange(this.meta, this.recordType, o);
12157 return this.readRecords(o);
12160 // private function a store will implement
12161 onMetaChange : function(meta, recordType, o){
12168 simpleAccess: function(obj, subsc) {
12175 getJsonAccessor: function(){
12177 return function(expr) {
12179 return(re.test(expr))
12180 ? new Function("obj", "return obj." + expr)
12185 return Roo.emptyFn;
12190 * Create a data block containing Roo.data.Records from an XML document.
12191 * @param {Object} o An object which contains an Array of row objects in the property specified
12192 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12193 * which contains the total size of the dataset.
12194 * @return {Object} data A data block which is used by an Roo.data.Store object as
12195 * a cache of Roo.data.Records.
12197 readRecords : function(o){
12199 * After any data loads, the raw JSON data is available for further custom processing.
12203 var s = this.meta, Record = this.recordType,
12204 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12206 // Generate extraction functions for the totalProperty, the root, the id, and for each field
12208 if(s.totalProperty) {
12209 this.getTotal = this.getJsonAccessor(s.totalProperty);
12211 if(s.successProperty) {
12212 this.getSuccess = this.getJsonAccessor(s.successProperty);
12214 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12216 var g = this.getJsonAccessor(s.id);
12217 this.getId = function(rec) {
12219 return (r === undefined || r === "") ? null : r;
12222 this.getId = function(){return null;};
12225 for(var jj = 0; jj < fl; jj++){
12227 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12228 this.ef[jj] = this.getJsonAccessor(map);
12232 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12233 if(s.totalProperty){
12234 var vt = parseInt(this.getTotal(o), 10);
12239 if(s.successProperty){
12240 var vs = this.getSuccess(o);
12241 if(vs === false || vs === 'false'){
12246 for(var i = 0; i < c; i++){
12249 var id = this.getId(n);
12250 for(var j = 0; j < fl; j++){
12252 var v = this.ef[j](n);
12254 Roo.log('missing convert for ' + f.name);
12258 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12260 var record = new Record(values, id);
12262 records[i] = record;
12268 totalRecords : totalRecords
12273 * Ext JS Library 1.1.1
12274 * Copyright(c) 2006-2007, Ext JS, LLC.
12276 * Originally Released Under LGPL - original licence link has changed is not relivant.
12279 * <script type="text/javascript">
12283 * @class Roo.data.ArrayReader
12284 * @extends Roo.data.DataReader
12285 * Data reader class to create an Array of Roo.data.Record objects from an Array.
12286 * Each element of that Array represents a row of data fields. The
12287 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12288 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12292 var RecordDef = Roo.data.Record.create([
12293 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
12294 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
12296 var myReader = new Roo.data.ArrayReader({
12297 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
12301 * This would consume an Array like this:
12303 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12305 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12307 * Create a new JsonReader
12308 * @param {Object} meta Metadata configuration options.
12309 * @param {Object} recordType Either an Array of field definition objects
12310 * as specified to {@link Roo.data.Record#create},
12311 * or an {@link Roo.data.Record} object
12312 * created using {@link Roo.data.Record#create}.
12314 Roo.data.ArrayReader = function(meta, recordType){
12315 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12318 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12320 * Create a data block containing Roo.data.Records from an XML document.
12321 * @param {Object} o An Array of row objects which represents the dataset.
12322 * @return {Object} data A data block which is used by an Roo.data.Store object as
12323 * a cache of Roo.data.Records.
12325 readRecords : function(o){
12326 var sid = this.meta ? this.meta.id : null;
12327 var recordType = this.recordType, fields = recordType.prototype.fields;
12330 for(var i = 0; i < root.length; i++){
12333 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12334 for(var j = 0, jlen = fields.length; j < jlen; j++){
12335 var f = fields.items[j];
12336 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12337 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12339 values[f.name] = v;
12341 var record = new recordType(values, id);
12343 records[records.length] = record;
12347 totalRecords : records.length
12356 * @class Roo.bootstrap.ComboBox
12357 * @extends Roo.bootstrap.TriggerField
12358 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12359 * @cfg {Boolean} append (true|false) default false
12360 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12361 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12362 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12363 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12364 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12365 * @cfg {Boolean} animate default true
12366 * @cfg {Boolean} emptyResultText only for touch device
12367 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12368 * @cfg {String} emptyTitle default ''
12370 * Create a new ComboBox.
12371 * @param {Object} config Configuration options
12373 Roo.bootstrap.ComboBox = function(config){
12374 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12378 * Fires when the dropdown list is expanded
12379 * @param {Roo.bootstrap.ComboBox} combo This combo box
12384 * Fires when the dropdown list is collapsed
12385 * @param {Roo.bootstrap.ComboBox} combo This combo box
12389 * @event beforeselect
12390 * Fires before a list item is selected. Return false to cancel the selection.
12391 * @param {Roo.bootstrap.ComboBox} combo This combo box
12392 * @param {Roo.data.Record} record The data record returned from the underlying store
12393 * @param {Number} index The index of the selected item in the dropdown list
12395 'beforeselect' : true,
12398 * Fires when a list item is selected
12399 * @param {Roo.bootstrap.ComboBox} combo This combo box
12400 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12401 * @param {Number} index The index of the selected item in the dropdown list
12405 * @event beforequery
12406 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12407 * The event object passed has these properties:
12408 * @param {Roo.bootstrap.ComboBox} combo This combo box
12409 * @param {String} query The query
12410 * @param {Boolean} forceAll true to force "all" query
12411 * @param {Boolean} cancel true to cancel the query
12412 * @param {Object} e The query event object
12414 'beforequery': true,
12417 * Fires when the 'add' icon is pressed (add a listener to enable add button)
12418 * @param {Roo.bootstrap.ComboBox} combo This combo box
12423 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12424 * @param {Roo.bootstrap.ComboBox} combo This combo box
12425 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12430 * Fires when the remove value from the combobox array
12431 * @param {Roo.bootstrap.ComboBox} combo This combo box
12435 * @event afterremove
12436 * Fires when the remove value from the combobox array
12437 * @param {Roo.bootstrap.ComboBox} combo This combo box
12439 'afterremove' : true,
12441 * @event specialfilter
12442 * Fires when specialfilter
12443 * @param {Roo.bootstrap.ComboBox} combo This combo box
12445 'specialfilter' : true,
12448 * Fires when tick the element
12449 * @param {Roo.bootstrap.ComboBox} combo This combo box
12453 * @event touchviewdisplay
12454 * Fires when touch view require special display (default is using displayField)
12455 * @param {Roo.bootstrap.ComboBox} combo This combo box
12456 * @param {Object} cfg set html .
12458 'touchviewdisplay' : true
12463 this.tickItems = [];
12465 this.selectedIndex = -1;
12466 if(this.mode == 'local'){
12467 if(config.queryDelay === undefined){
12468 this.queryDelay = 10;
12470 if(config.minChars === undefined){
12476 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12479 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12480 * rendering into an Roo.Editor, defaults to false)
12483 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12484 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12487 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12490 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12491 * the dropdown list (defaults to undefined, with no header element)
12495 * @cfg {String/Roo.Template} tpl The template to use to render the output
12499 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12501 listWidth: undefined,
12503 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12504 * mode = 'remote' or 'text' if mode = 'local')
12506 displayField: undefined,
12509 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12510 * mode = 'remote' or 'value' if mode = 'local').
12511 * Note: use of a valueField requires the user make a selection
12512 * in order for a value to be mapped.
12514 valueField: undefined,
12516 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12521 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12522 * field's data value (defaults to the underlying DOM element's name)
12524 hiddenName: undefined,
12526 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12530 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12532 selectedClass: 'active',
12535 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12539 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12540 * anchor positions (defaults to 'tl-bl')
12542 listAlign: 'tl-bl?',
12544 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12548 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
12549 * query specified by the allQuery config option (defaults to 'query')
12551 triggerAction: 'query',
12553 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12554 * (defaults to 4, does not apply if editable = false)
12558 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12559 * delay (typeAheadDelay) if it matches a known value (defaults to false)
12563 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12564 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12568 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12569 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
12573 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
12574 * when editable = true (defaults to false)
12576 selectOnFocus:false,
12578 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12580 queryParam: 'query',
12582 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
12583 * when mode = 'remote' (defaults to 'Loading...')
12585 loadingText: 'Loading...',
12587 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12591 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12595 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12596 * traditional select (defaults to true)
12600 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12604 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12608 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12609 * listWidth has a higher value)
12613 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12614 * allow the user to set arbitrary text into the field (defaults to false)
12616 forceSelection:false,
12618 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12619 * if typeAhead = true (defaults to 250)
12621 typeAheadDelay : 250,
12623 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12624 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12626 valueNotFoundText : undefined,
12628 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12630 blockFocus : false,
12633 * @cfg {Boolean} disableClear Disable showing of clear button.
12635 disableClear : false,
12637 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
12639 alwaysQuery : false,
12642 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
12647 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12649 invalidClass : "has-warning",
12652 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12654 validClass : "has-success",
12657 * @cfg {Boolean} specialFilter (true|false) special filter default false
12659 specialFilter : false,
12662 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12664 mobileTouchView : true,
12667 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12669 useNativeIOS : false,
12671 ios_options : false,
12683 btnPosition : 'right',
12684 triggerList : true,
12685 showToggleBtn : true,
12687 emptyResultText: 'Empty',
12688 triggerText : 'Select',
12691 // element that contains real text value.. (when hidden is used..)
12693 getAutoCreate : function()
12698 * Render classic select for iso
12701 if(Roo.isIOS && this.useNativeIOS){
12702 cfg = this.getAutoCreateNativeIOS();
12710 if(Roo.isTouch && this.mobileTouchView){
12711 cfg = this.getAutoCreateTouchView();
12718 if(!this.tickable){
12719 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12724 * ComboBox with tickable selections
12727 var align = this.labelAlign || this.parentLabelAlign();
12730 cls : 'form-group roo-combobox-tickable' //input-group
12733 var btn_text_select = '';
12734 var btn_text_done = '';
12735 var btn_text_cancel = '';
12737 if (this.btn_text_show) {
12738 btn_text_select = 'Select';
12739 btn_text_done = 'Done';
12740 btn_text_cancel = 'Cancel';
12745 cls : 'tickable-buttons',
12750 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12751 //html : this.triggerText
12752 html: btn_text_select
12758 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12760 html: btn_text_done
12766 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12768 html: btn_text_cancel
12774 buttons.cn.unshift({
12776 cls: 'roo-select2-search-field-input'
12782 Roo.each(buttons.cn, function(c){
12784 c.cls += ' btn-' + _this.size;
12787 if (_this.disabled) {
12798 cls: 'form-hidden-field'
12802 cls: 'roo-select2-choices',
12806 cls: 'roo-select2-search-field',
12817 cls: 'roo-select2-container input-group roo-select2-container-multi',
12822 // cls: 'typeahead typeahead-long dropdown-menu',
12823 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
12828 if(this.hasFeedback && !this.allowBlank){
12832 cls: 'glyphicon form-control-feedback'
12835 combobox.cn.push(feedback);
12839 if (align ==='left' && this.fieldLabel.length) {
12841 cfg.cls += ' roo-form-group-label-left';
12846 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12847 tooltip : 'This field is required'
12852 cls : 'control-label',
12853 html : this.fieldLabel
12865 var labelCfg = cfg.cn[1];
12866 var contentCfg = cfg.cn[2];
12869 if(this.indicatorpos == 'right'){
12875 cls : 'control-label',
12879 html : this.fieldLabel
12883 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12884 tooltip : 'This field is required'
12899 labelCfg = cfg.cn[0];
12900 contentCfg = cfg.cn[1];
12904 if(this.labelWidth > 12){
12905 labelCfg.style = "width: " + this.labelWidth + 'px';
12908 if(this.labelWidth < 13 && this.labelmd == 0){
12909 this.labelmd = this.labelWidth;
12912 if(this.labellg > 0){
12913 labelCfg.cls += ' col-lg-' + this.labellg;
12914 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12917 if(this.labelmd > 0){
12918 labelCfg.cls += ' col-md-' + this.labelmd;
12919 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12922 if(this.labelsm > 0){
12923 labelCfg.cls += ' col-sm-' + this.labelsm;
12924 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12927 if(this.labelxs > 0){
12928 labelCfg.cls += ' col-xs-' + this.labelxs;
12929 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12933 } else if ( this.fieldLabel.length) {
12934 // Roo.log(" label");
12938 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12939 tooltip : 'This field is required'
12943 //cls : 'input-group-addon',
12944 html : this.fieldLabel
12949 if(this.indicatorpos == 'right'){
12953 //cls : 'input-group-addon',
12954 html : this.fieldLabel
12958 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12959 tooltip : 'This field is required'
12968 // Roo.log(" no label && no align");
12975 ['xs','sm','md','lg'].map(function(size){
12976 if (settings[size]) {
12977 cfg.cls += ' col-' + size + '-' + settings[size];
12985 _initEventsCalled : false,
12988 initEvents: function()
12990 if (this._initEventsCalled) { // as we call render... prevent looping...
12993 this._initEventsCalled = true;
12996 throw "can not find store for combo";
12999 this.store = Roo.factory(this.store, Roo.data);
13000 this.store.parent = this;
13002 // if we are building from html. then this element is so complex, that we can not really
13003 // use the rendered HTML.
13004 // so we have to trash and replace the previous code.
13005 if (Roo.XComponent.build_from_html) {
13006 // remove this element....
13007 var e = this.el.dom, k=0;
13008 while (e ) { e = e.previousSibling; ++k;}
13013 this.rendered = false;
13015 this.render(this.parent().getChildContainer(true), k);
13018 if(Roo.isIOS && this.useNativeIOS){
13019 this.initIOSView();
13027 if(Roo.isTouch && this.mobileTouchView){
13028 this.initTouchView();
13033 this.initTickableEvents();
13037 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13039 if(this.hiddenName){
13041 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13043 this.hiddenField.dom.value =
13044 this.hiddenValue !== undefined ? this.hiddenValue :
13045 this.value !== undefined ? this.value : '';
13047 // prevent input submission
13048 this.el.dom.removeAttribute('name');
13049 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13054 // this.el.dom.setAttribute('autocomplete', 'off');
13057 var cls = 'x-combo-list';
13059 //this.list = new Roo.Layer({
13060 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13066 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13067 _this.list.setWidth(lw);
13070 this.list.on('mouseover', this.onViewOver, this);
13071 this.list.on('mousemove', this.onViewMove, this);
13072 this.list.on('scroll', this.onViewScroll, this);
13075 this.list.swallowEvent('mousewheel');
13076 this.assetHeight = 0;
13079 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13080 this.assetHeight += this.header.getHeight();
13083 this.innerList = this.list.createChild({cls:cls+'-inner'});
13084 this.innerList.on('mouseover', this.onViewOver, this);
13085 this.innerList.on('mousemove', this.onViewMove, this);
13086 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13088 if(this.allowBlank && !this.pageSize && !this.disableClear){
13089 this.footer = this.list.createChild({cls:cls+'-ft'});
13090 this.pageTb = new Roo.Toolbar(this.footer);
13094 this.footer = this.list.createChild({cls:cls+'-ft'});
13095 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13096 {pageSize: this.pageSize});
13100 if (this.pageTb && this.allowBlank && !this.disableClear) {
13102 this.pageTb.add(new Roo.Toolbar.Fill(), {
13103 cls: 'x-btn-icon x-btn-clear',
13105 handler: function()
13108 _this.clearValue();
13109 _this.onSelect(false, -1);
13114 this.assetHeight += this.footer.getHeight();
13119 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13122 this.view = new Roo.View(this.list, this.tpl, {
13123 singleSelect:true, store: this.store, selectedClass: this.selectedClass
13125 //this.view.wrapEl.setDisplayed(false);
13126 this.view.on('click', this.onViewClick, this);
13129 this.store.on('beforeload', this.onBeforeLoad, this);
13130 this.store.on('load', this.onLoad, this);
13131 this.store.on('loadexception', this.onLoadException, this);
13133 if(this.resizable){
13134 this.resizer = new Roo.Resizable(this.list, {
13135 pinned:true, handles:'se'
13137 this.resizer.on('resize', function(r, w, h){
13138 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13139 this.listWidth = w;
13140 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13141 this.restrictHeight();
13143 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13146 if(!this.editable){
13147 this.editable = true;
13148 this.setEditable(false);
13153 if (typeof(this.events.add.listeners) != 'undefined') {
13155 this.addicon = this.wrap.createChild(
13156 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
13158 this.addicon.on('click', function(e) {
13159 this.fireEvent('add', this);
13162 if (typeof(this.events.edit.listeners) != 'undefined') {
13164 this.editicon = this.wrap.createChild(
13165 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
13166 if (this.addicon) {
13167 this.editicon.setStyle('margin-left', '40px');
13169 this.editicon.on('click', function(e) {
13171 // we fire even if inothing is selected..
13172 this.fireEvent('edit', this, this.lastData );
13178 this.keyNav = new Roo.KeyNav(this.inputEl(), {
13179 "up" : function(e){
13180 this.inKeyMode = true;
13184 "down" : function(e){
13185 if(!this.isExpanded()){
13186 this.onTriggerClick();
13188 this.inKeyMode = true;
13193 "enter" : function(e){
13194 // this.onViewClick();
13198 if(this.fireEvent("specialkey", this, e)){
13199 this.onViewClick(false);
13205 "esc" : function(e){
13209 "tab" : function(e){
13212 if(this.fireEvent("specialkey", this, e)){
13213 this.onViewClick(false);
13221 doRelay : function(foo, bar, hname){
13222 if(hname == 'down' || this.scope.isExpanded()){
13223 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13232 this.queryDelay = Math.max(this.queryDelay || 10,
13233 this.mode == 'local' ? 10 : 250);
13236 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13238 if(this.typeAhead){
13239 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13241 if(this.editable !== false){
13242 this.inputEl().on("keyup", this.onKeyUp, this);
13244 if(this.forceSelection){
13245 this.inputEl().on('blur', this.doForce, this);
13249 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13250 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13254 initTickableEvents: function()
13258 if(this.hiddenName){
13260 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13262 this.hiddenField.dom.value =
13263 this.hiddenValue !== undefined ? this.hiddenValue :
13264 this.value !== undefined ? this.value : '';
13266 // prevent input submission
13267 this.el.dom.removeAttribute('name');
13268 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13273 // this.list = this.el.select('ul.dropdown-menu',true).first();
13275 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13276 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13277 if(this.triggerList){
13278 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13281 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13282 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13284 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13285 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13287 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13288 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13290 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13291 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13292 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13295 this.cancelBtn.hide();
13300 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13301 _this.list.setWidth(lw);
13304 this.list.on('mouseover', this.onViewOver, this);
13305 this.list.on('mousemove', this.onViewMove, this);
13307 this.list.on('scroll', this.onViewScroll, this);
13310 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></div></li>';
13313 this.view = new Roo.View(this.list, this.tpl, {
13314 singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
13317 //this.view.wrapEl.setDisplayed(false);
13318 this.view.on('click', this.onViewClick, this);
13322 this.store.on('beforeload', this.onBeforeLoad, this);
13323 this.store.on('load', this.onLoad, this);
13324 this.store.on('loadexception', this.onLoadException, this);
13327 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13328 "up" : function(e){
13329 this.inKeyMode = true;
13333 "down" : function(e){
13334 this.inKeyMode = true;
13338 "enter" : function(e){
13339 if(this.fireEvent("specialkey", this, e)){
13340 this.onViewClick(false);
13346 "esc" : function(e){
13347 this.onTickableFooterButtonClick(e, false, false);
13350 "tab" : function(e){
13351 this.fireEvent("specialkey", this, e);
13353 this.onTickableFooterButtonClick(e, false, false);
13360 doRelay : function(e, fn, key){
13361 if(this.scope.isExpanded()){
13362 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13371 this.queryDelay = Math.max(this.queryDelay || 10,
13372 this.mode == 'local' ? 10 : 250);
13375 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13377 if(this.typeAhead){
13378 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13381 if(this.editable !== false){
13382 this.tickableInputEl().on("keyup", this.onKeyUp, this);
13385 this.indicator = this.indicatorEl();
13387 if(this.indicator){
13388 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13389 this.indicator.hide();
13394 onDestroy : function(){
13396 this.view.setStore(null);
13397 this.view.el.removeAllListeners();
13398 this.view.el.remove();
13399 this.view.purgeListeners();
13402 this.list.dom.innerHTML = '';
13406 this.store.un('beforeload', this.onBeforeLoad, this);
13407 this.store.un('load', this.onLoad, this);
13408 this.store.un('loadexception', this.onLoadException, this);
13410 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13414 fireKey : function(e){
13415 if(e.isNavKeyPress() && !this.list.isVisible()){
13416 this.fireEvent("specialkey", this, e);
13421 onResize: function(w, h){
13422 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13424 // if(typeof w != 'number'){
13425 // // we do not handle it!?!?
13428 // var tw = this.trigger.getWidth();
13429 // // tw += this.addicon ? this.addicon.getWidth() : 0;
13430 // // tw += this.editicon ? this.editicon.getWidth() : 0;
13432 // this.inputEl().setWidth( this.adjustWidth('input', x));
13434 // //this.trigger.setStyle('left', x+'px');
13436 // if(this.list && this.listWidth === undefined){
13437 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13438 // this.list.setWidth(lw);
13439 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13447 * Allow or prevent the user from directly editing the field text. If false is passed,
13448 * the user will only be able to select from the items defined in the dropdown list. This method
13449 * is the runtime equivalent of setting the 'editable' config option at config time.
13450 * @param {Boolean} value True to allow the user to directly edit the field text
13452 setEditable : function(value){
13453 if(value == this.editable){
13456 this.editable = value;
13458 this.inputEl().dom.setAttribute('readOnly', true);
13459 this.inputEl().on('mousedown', this.onTriggerClick, this);
13460 this.inputEl().addClass('x-combo-noedit');
13462 this.inputEl().dom.setAttribute('readOnly', false);
13463 this.inputEl().un('mousedown', this.onTriggerClick, this);
13464 this.inputEl().removeClass('x-combo-noedit');
13470 onBeforeLoad : function(combo,opts){
13471 if(!this.hasFocus){
13475 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13477 this.restrictHeight();
13478 this.selectedIndex = -1;
13482 onLoad : function(){
13484 this.hasQuery = false;
13486 if(!this.hasFocus){
13490 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13491 this.loading.hide();
13494 if(this.store.getCount() > 0){
13497 this.restrictHeight();
13498 if(this.lastQuery == this.allQuery){
13499 if(this.editable && !this.tickable){
13500 this.inputEl().dom.select();
13504 !this.selectByValue(this.value, true) &&
13507 !this.store.lastOptions ||
13508 typeof(this.store.lastOptions.add) == 'undefined' ||
13509 this.store.lastOptions.add != true
13512 this.select(0, true);
13515 if(this.autoFocus){
13518 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13519 this.taTask.delay(this.typeAheadDelay);
13523 this.onEmptyResults();
13529 onLoadException : function()
13531 this.hasQuery = false;
13533 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13534 this.loading.hide();
13537 if(this.tickable && this.editable){
13542 // only causes errors at present
13543 //Roo.log(this.store.reader.jsonData);
13544 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13546 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13552 onTypeAhead : function(){
13553 if(this.store.getCount() > 0){
13554 var r = this.store.getAt(0);
13555 var newValue = r.data[this.displayField];
13556 var len = newValue.length;
13557 var selStart = this.getRawValue().length;
13559 if(selStart != len){
13560 this.setRawValue(newValue);
13561 this.selectText(selStart, newValue.length);
13567 onSelect : function(record, index){
13569 if(this.fireEvent('beforeselect', this, record, index) !== false){
13571 this.setFromData(index > -1 ? record.data : false);
13574 this.fireEvent('select', this, record, index);
13579 * Returns the currently selected field value or empty string if no value is set.
13580 * @return {String} value The selected value
13582 getValue : function()
13584 if(Roo.isIOS && this.useNativeIOS){
13585 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13589 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13592 if(this.valueField){
13593 return typeof this.value != 'undefined' ? this.value : '';
13595 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13599 getRawValue : function()
13601 if(Roo.isIOS && this.useNativeIOS){
13602 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13605 var v = this.inputEl().getValue();
13611 * Clears any text/value currently set in the field
13613 clearValue : function(){
13615 if(this.hiddenField){
13616 this.hiddenField.dom.value = '';
13619 this.setRawValue('');
13620 this.lastSelectionText = '';
13621 this.lastData = false;
13623 var close = this.closeTriggerEl();
13634 * Sets the specified value into the field. If the value finds a match, the corresponding record text
13635 * will be displayed in the field. If the value does not match the data value of an existing item,
13636 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13637 * Otherwise the field will be blank (although the value will still be set).
13638 * @param {String} value The value to match
13640 setValue : function(v)
13642 if(Roo.isIOS && this.useNativeIOS){
13643 this.setIOSValue(v);
13653 if(this.valueField){
13654 var r = this.findRecord(this.valueField, v);
13656 text = r.data[this.displayField];
13657 }else if(this.valueNotFoundText !== undefined){
13658 text = this.valueNotFoundText;
13661 this.lastSelectionText = text;
13662 if(this.hiddenField){
13663 this.hiddenField.dom.value = v;
13665 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13668 var close = this.closeTriggerEl();
13671 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13677 * @property {Object} the last set data for the element
13682 * Sets the value of the field based on a object which is related to the record format for the store.
13683 * @param {Object} value the value to set as. or false on reset?
13685 setFromData : function(o){
13692 var dv = ''; // display value
13693 var vv = ''; // value value..
13695 if (this.displayField) {
13696 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13698 // this is an error condition!!!
13699 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
13702 if(this.valueField){
13703 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13706 var close = this.closeTriggerEl();
13709 if(dv.length || vv * 1 > 0){
13711 this.blockFocus=true;
13717 if(this.hiddenField){
13718 this.hiddenField.dom.value = vv;
13720 this.lastSelectionText = dv;
13721 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13725 // no hidden field.. - we store the value in 'value', but still display
13726 // display field!!!!
13727 this.lastSelectionText = dv;
13728 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13735 reset : function(){
13736 // overridden so that last data is reset..
13743 this.setValue(this.originalValue);
13744 //this.clearInvalid();
13745 this.lastData = false;
13747 this.view.clearSelections();
13753 findRecord : function(prop, value){
13755 if(this.store.getCount() > 0){
13756 this.store.each(function(r){
13757 if(r.data[prop] == value){
13767 getName: function()
13769 // returns hidden if it's set..
13770 if (!this.rendered) {return ''};
13771 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
13775 onViewMove : function(e, t){
13776 this.inKeyMode = false;
13780 onViewOver : function(e, t){
13781 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13784 var item = this.view.findItemFromChild(t);
13787 var index = this.view.indexOf(item);
13788 this.select(index, false);
13793 onViewClick : function(view, doFocus, el, e)
13795 var index = this.view.getSelectedIndexes()[0];
13797 var r = this.store.getAt(index);
13801 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13808 Roo.each(this.tickItems, function(v,k){
13810 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13812 _this.tickItems.splice(k, 1);
13814 if(typeof(e) == 'undefined' && view == false){
13815 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13827 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13828 this.tickItems.push(r.data);
13831 if(typeof(e) == 'undefined' && view == false){
13832 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13839 this.onSelect(r, index);
13841 if(doFocus !== false && !this.blockFocus){
13842 this.inputEl().focus();
13847 restrictHeight : function(){
13848 //this.innerList.dom.style.height = '';
13849 //var inner = this.innerList.dom;
13850 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13851 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13852 //this.list.beginUpdate();
13853 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13854 this.list.alignTo(this.inputEl(), this.listAlign);
13855 this.list.alignTo(this.inputEl(), this.listAlign);
13856 //this.list.endUpdate();
13860 onEmptyResults : function(){
13862 if(this.tickable && this.editable){
13863 this.restrictHeight();
13871 * Returns true if the dropdown list is expanded, else false.
13873 isExpanded : function(){
13874 return this.list.isVisible();
13878 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13879 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13880 * @param {String} value The data value of the item to select
13881 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13882 * selected item if it is not currently in view (defaults to true)
13883 * @return {Boolean} True if the value matched an item in the list, else false
13885 selectByValue : function(v, scrollIntoView){
13886 if(v !== undefined && v !== null){
13887 var r = this.findRecord(this.valueField || this.displayField, v);
13889 this.select(this.store.indexOf(r), scrollIntoView);
13897 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13898 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13899 * @param {Number} index The zero-based index of the list item to select
13900 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13901 * selected item if it is not currently in view (defaults to true)
13903 select : function(index, scrollIntoView){
13904 this.selectedIndex = index;
13905 this.view.select(index);
13906 if(scrollIntoView !== false){
13907 var el = this.view.getNode(index);
13909 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13912 this.list.scrollChildIntoView(el, false);
13918 selectNext : function(){
13919 var ct = this.store.getCount();
13921 if(this.selectedIndex == -1){
13923 }else if(this.selectedIndex < ct-1){
13924 this.select(this.selectedIndex+1);
13930 selectPrev : function(){
13931 var ct = this.store.getCount();
13933 if(this.selectedIndex == -1){
13935 }else if(this.selectedIndex != 0){
13936 this.select(this.selectedIndex-1);
13942 onKeyUp : function(e){
13943 if(this.editable !== false && !e.isSpecialKey()){
13944 this.lastKey = e.getKey();
13945 this.dqTask.delay(this.queryDelay);
13950 validateBlur : function(){
13951 return !this.list || !this.list.isVisible();
13955 initQuery : function(){
13957 var v = this.getRawValue();
13959 if(this.tickable && this.editable){
13960 v = this.tickableInputEl().getValue();
13967 doForce : function(){
13968 if(this.inputEl().dom.value.length > 0){
13969 this.inputEl().dom.value =
13970 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
13976 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
13977 * query allowing the query action to be canceled if needed.
13978 * @param {String} query The SQL query to execute
13979 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
13980 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
13981 * saved in the current store (defaults to false)
13983 doQuery : function(q, forceAll){
13985 if(q === undefined || q === null){
13990 forceAll: forceAll,
13994 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
13999 forceAll = qe.forceAll;
14000 if(forceAll === true || (q.length >= this.minChars)){
14002 this.hasQuery = true;
14004 if(this.lastQuery != q || this.alwaysQuery){
14005 this.lastQuery = q;
14006 if(this.mode == 'local'){
14007 this.selectedIndex = -1;
14009 this.store.clearFilter();
14012 if(this.specialFilter){
14013 this.fireEvent('specialfilter', this);
14018 this.store.filter(this.displayField, q);
14021 this.store.fireEvent("datachanged", this.store);
14028 this.store.baseParams[this.queryParam] = q;
14030 var options = {params : this.getParams(q)};
14033 options.add = true;
14034 options.params.start = this.page * this.pageSize;
14037 this.store.load(options);
14040 * this code will make the page width larger, at the beginning, the list not align correctly,
14041 * we should expand the list on onLoad
14042 * so command out it
14047 this.selectedIndex = -1;
14052 this.loadNext = false;
14056 getParams : function(q){
14058 //p[this.queryParam] = q;
14062 p.limit = this.pageSize;
14068 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14070 collapse : function(){
14071 if(!this.isExpanded()){
14077 this.hasFocus = false;
14081 this.cancelBtn.hide();
14082 this.trigger.show();
14085 this.tickableInputEl().dom.value = '';
14086 this.tickableInputEl().blur();
14091 Roo.get(document).un('mousedown', this.collapseIf, this);
14092 Roo.get(document).un('mousewheel', this.collapseIf, this);
14093 if (!this.editable) {
14094 Roo.get(document).un('keydown', this.listKeyPress, this);
14096 this.fireEvent('collapse', this);
14102 collapseIf : function(e){
14103 var in_combo = e.within(this.el);
14104 var in_list = e.within(this.list);
14105 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14107 if (in_combo || in_list || is_list) {
14108 //e.stopPropagation();
14113 this.onTickableFooterButtonClick(e, false, false);
14121 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14123 expand : function(){
14125 if(this.isExpanded() || !this.hasFocus){
14129 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14130 this.list.setWidth(lw);
14136 this.restrictHeight();
14140 this.tickItems = Roo.apply([], this.item);
14143 this.cancelBtn.show();
14144 this.trigger.hide();
14147 this.tickableInputEl().focus();
14152 Roo.get(document).on('mousedown', this.collapseIf, this);
14153 Roo.get(document).on('mousewheel', this.collapseIf, this);
14154 if (!this.editable) {
14155 Roo.get(document).on('keydown', this.listKeyPress, this);
14158 this.fireEvent('expand', this);
14162 // Implements the default empty TriggerField.onTriggerClick function
14163 onTriggerClick : function(e)
14165 Roo.log('trigger click');
14167 if(this.disabled || !this.triggerList){
14172 this.loadNext = false;
14174 if(this.isExpanded()){
14176 if (!this.blockFocus) {
14177 this.inputEl().focus();
14181 this.hasFocus = true;
14182 if(this.triggerAction == 'all') {
14183 this.doQuery(this.allQuery, true);
14185 this.doQuery(this.getRawValue());
14187 if (!this.blockFocus) {
14188 this.inputEl().focus();
14193 onTickableTriggerClick : function(e)
14200 this.loadNext = false;
14201 this.hasFocus = true;
14203 if(this.triggerAction == 'all') {
14204 this.doQuery(this.allQuery, true);
14206 this.doQuery(this.getRawValue());
14210 onSearchFieldClick : function(e)
14212 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14213 this.onTickableFooterButtonClick(e, false, false);
14217 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14222 this.loadNext = false;
14223 this.hasFocus = true;
14225 if(this.triggerAction == 'all') {
14226 this.doQuery(this.allQuery, true);
14228 this.doQuery(this.getRawValue());
14232 listKeyPress : function(e)
14234 //Roo.log('listkeypress');
14235 // scroll to first matching element based on key pres..
14236 if (e.isSpecialKey()) {
14239 var k = String.fromCharCode(e.getKey()).toUpperCase();
14242 var csel = this.view.getSelectedNodes();
14243 var cselitem = false;
14245 var ix = this.view.indexOf(csel[0]);
14246 cselitem = this.store.getAt(ix);
14247 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14253 this.store.each(function(v) {
14255 // start at existing selection.
14256 if (cselitem.id == v.id) {
14262 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14263 match = this.store.indexOf(v);
14269 if (match === false) {
14270 return true; // no more action?
14273 this.view.select(match);
14274 var sn = Roo.get(this.view.getSelectedNodes()[0]);
14275 sn.scrollIntoView(sn.dom.parentNode, false);
14278 onViewScroll : function(e, t){
14280 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){
14284 this.hasQuery = true;
14286 this.loading = this.list.select('.loading', true).first();
14288 if(this.loading === null){
14289 this.list.createChild({
14291 cls: 'loading roo-select2-more-results roo-select2-active',
14292 html: 'Loading more results...'
14295 this.loading = this.list.select('.loading', true).first();
14297 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14299 this.loading.hide();
14302 this.loading.show();
14307 this.loadNext = true;
14309 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14314 addItem : function(o)
14316 var dv = ''; // display value
14318 if (this.displayField) {
14319 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14321 // this is an error condition!!!
14322 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14329 var choice = this.choices.createChild({
14331 cls: 'roo-select2-search-choice',
14340 cls: 'roo-select2-search-choice-close fa fa-times',
14345 }, this.searchField);
14347 var close = choice.select('a.roo-select2-search-choice-close', true).first();
14349 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14357 this.inputEl().dom.value = '';
14362 onRemoveItem : function(e, _self, o)
14364 e.preventDefault();
14366 this.lastItem = Roo.apply([], this.item);
14368 var index = this.item.indexOf(o.data) * 1;
14371 Roo.log('not this item?!');
14375 this.item.splice(index, 1);
14380 this.fireEvent('remove', this, e);
14386 syncValue : function()
14388 if(!this.item.length){
14395 Roo.each(this.item, function(i){
14396 if(_this.valueField){
14397 value.push(i[_this.valueField]);
14404 this.value = value.join(',');
14406 if(this.hiddenField){
14407 this.hiddenField.dom.value = this.value;
14410 this.store.fireEvent("datachanged", this.store);
14415 clearItem : function()
14417 if(!this.multiple){
14423 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14431 if(this.tickable && !Roo.isTouch){
14432 this.view.refresh();
14436 inputEl: function ()
14438 if(Roo.isIOS && this.useNativeIOS){
14439 return this.el.select('select.roo-ios-select', true).first();
14442 if(Roo.isTouch && this.mobileTouchView){
14443 return this.el.select('input.form-control',true).first();
14447 return this.searchField;
14450 return this.el.select('input.form-control',true).first();
14453 onTickableFooterButtonClick : function(e, btn, el)
14455 e.preventDefault();
14457 this.lastItem = Roo.apply([], this.item);
14459 if(btn && btn.name == 'cancel'){
14460 this.tickItems = Roo.apply([], this.item);
14469 Roo.each(this.tickItems, function(o){
14477 validate : function()
14479 var v = this.getRawValue();
14482 v = this.getValue();
14485 if(this.disabled || this.allowBlank || v.length){
14490 this.markInvalid();
14494 tickableInputEl : function()
14496 if(!this.tickable || !this.editable){
14497 return this.inputEl();
14500 return this.inputEl().select('.roo-select2-search-field-input', true).first();
14504 getAutoCreateTouchView : function()
14509 cls: 'form-group' //input-group
14515 type : this.inputType,
14516 cls : 'form-control x-combo-noedit',
14517 autocomplete: 'new-password',
14518 placeholder : this.placeholder || '',
14523 input.name = this.name;
14527 input.cls += ' input-' + this.size;
14530 if (this.disabled) {
14531 input.disabled = true;
14542 inputblock.cls += ' input-group';
14544 inputblock.cn.unshift({
14546 cls : 'input-group-addon',
14551 if(this.removable && !this.multiple){
14552 inputblock.cls += ' roo-removable';
14554 inputblock.cn.push({
14557 cls : 'roo-combo-removable-btn close'
14561 if(this.hasFeedback && !this.allowBlank){
14563 inputblock.cls += ' has-feedback';
14565 inputblock.cn.push({
14567 cls: 'glyphicon form-control-feedback'
14574 inputblock.cls += (this.before) ? '' : ' input-group';
14576 inputblock.cn.push({
14578 cls : 'input-group-addon',
14589 cls: 'form-hidden-field'
14603 cls: 'form-hidden-field'
14607 cls: 'roo-select2-choices',
14611 cls: 'roo-select2-search-field',
14624 cls: 'roo-select2-container input-group roo-touchview-combobox ',
14630 if(!this.multiple && this.showToggleBtn){
14637 if (this.caret != false) {
14640 cls: 'fa fa-' + this.caret
14647 cls : 'input-group-addon btn dropdown-toggle',
14652 cls: 'combobox-clear',
14666 combobox.cls += ' roo-select2-container-multi';
14669 var align = this.labelAlign || this.parentLabelAlign();
14671 if (align ==='left' && this.fieldLabel.length) {
14676 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14677 tooltip : 'This field is required'
14681 cls : 'control-label',
14682 html : this.fieldLabel
14693 var labelCfg = cfg.cn[1];
14694 var contentCfg = cfg.cn[2];
14697 if(this.indicatorpos == 'right'){
14701 cls : 'control-label',
14702 html : this.fieldLabel,
14706 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14707 tooltip : 'This field is required'
14718 labelCfg = cfg.cn[0];
14719 contentCfg = cfg.cn[1];
14724 if(this.labelWidth > 12){
14725 labelCfg.style = "width: " + this.labelWidth + 'px';
14728 if(this.labelWidth < 13 && this.labelmd == 0){
14729 this.labelmd = this.labelWidth;
14732 if(this.labellg > 0){
14733 labelCfg.cls += ' col-lg-' + this.labellg;
14734 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14737 if(this.labelmd > 0){
14738 labelCfg.cls += ' col-md-' + this.labelmd;
14739 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14742 if(this.labelsm > 0){
14743 labelCfg.cls += ' col-sm-' + this.labelsm;
14744 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14747 if(this.labelxs > 0){
14748 labelCfg.cls += ' col-xs-' + this.labelxs;
14749 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14753 } else if ( this.fieldLabel.length) {
14757 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14758 tooltip : 'This field is required'
14762 cls : 'control-label',
14763 html : this.fieldLabel
14774 if(this.indicatorpos == 'right'){
14778 cls : 'control-label',
14779 html : this.fieldLabel,
14783 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14784 tooltip : 'This field is required'
14801 var settings = this;
14803 ['xs','sm','md','lg'].map(function(size){
14804 if (settings[size]) {
14805 cfg.cls += ' col-' + size + '-' + settings[size];
14812 initTouchView : function()
14814 this.renderTouchView();
14816 this.touchViewEl.on('scroll', function(){
14817 this.el.dom.scrollTop = 0;
14820 this.originalValue = this.getValue();
14822 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14824 this.inputEl().on("click", this.showTouchView, this);
14825 if (this.triggerEl) {
14826 this.triggerEl.on("click", this.showTouchView, this);
14830 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14831 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14833 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14835 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14836 this.store.on('load', this.onTouchViewLoad, this);
14837 this.store.on('loadexception', this.onTouchViewLoadException, this);
14839 if(this.hiddenName){
14841 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14843 this.hiddenField.dom.value =
14844 this.hiddenValue !== undefined ? this.hiddenValue :
14845 this.value !== undefined ? this.value : '';
14847 this.el.dom.removeAttribute('name');
14848 this.hiddenField.dom.setAttribute('name', this.hiddenName);
14852 this.choices = this.el.select('ul.roo-select2-choices', true).first();
14853 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14856 if(this.removable && !this.multiple){
14857 var close = this.closeTriggerEl();
14859 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14860 close.on('click', this.removeBtnClick, this, close);
14864 * fix the bug in Safari iOS8
14866 this.inputEl().on("focus", function(e){
14867 document.activeElement.blur();
14875 renderTouchView : function()
14877 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14878 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14880 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14881 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14883 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14884 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14885 this.touchViewBodyEl.setStyle('overflow', 'auto');
14887 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14888 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14890 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14891 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14895 showTouchView : function()
14901 this.touchViewHeaderEl.hide();
14903 if(this.modalTitle.length){
14904 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14905 this.touchViewHeaderEl.show();
14908 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
14909 this.touchViewEl.show();
14911 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
14913 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14914 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14916 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14918 if(this.modalTitle.length){
14919 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14922 this.touchViewBodyEl.setHeight(bodyHeight);
14926 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14928 this.touchViewEl.addClass('in');
14931 this.doTouchViewQuery();
14935 hideTouchView : function()
14937 this.touchViewEl.removeClass('in');
14941 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14943 this.touchViewEl.setStyle('display', 'none');
14948 setTouchViewValue : function()
14955 Roo.each(this.tickItems, function(o){
14960 this.hideTouchView();
14963 doTouchViewQuery : function()
14972 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
14976 if(!this.alwaysQuery || this.mode == 'local'){
14977 this.onTouchViewLoad();
14984 onTouchViewBeforeLoad : function(combo,opts)
14990 onTouchViewLoad : function()
14992 if(this.store.getCount() < 1){
14993 this.onTouchViewEmptyResults();
14997 this.clearTouchView();
14999 var rawValue = this.getRawValue();
15001 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15003 this.tickItems = [];
15005 this.store.data.each(function(d, rowIndex){
15006 var row = this.touchViewListGroup.createChild(template);
15008 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15009 row.addClass(d.data.cls);
15012 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15015 html : d.data[this.displayField]
15018 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15019 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15022 row.removeClass('selected');
15023 if(!this.multiple && this.valueField &&
15024 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15027 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15028 row.addClass('selected');
15031 if(this.multiple && this.valueField &&
15032 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15036 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15037 this.tickItems.push(d.data);
15040 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15044 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15046 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15048 if(this.modalTitle.length){
15049 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15052 var listHeight = this.touchViewListGroup.getHeight();
15056 if(firstChecked && listHeight > bodyHeight){
15057 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15062 onTouchViewLoadException : function()
15064 this.hideTouchView();
15067 onTouchViewEmptyResults : function()
15069 this.clearTouchView();
15071 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15073 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15077 clearTouchView : function()
15079 this.touchViewListGroup.dom.innerHTML = '';
15082 onTouchViewClick : function(e, el, o)
15084 e.preventDefault();
15087 var rowIndex = o.rowIndex;
15089 var r = this.store.getAt(rowIndex);
15091 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15093 if(!this.multiple){
15094 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15095 c.dom.removeAttribute('checked');
15098 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15100 this.setFromData(r.data);
15102 var close = this.closeTriggerEl();
15108 this.hideTouchView();
15110 this.fireEvent('select', this, r, rowIndex);
15115 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15116 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15117 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15121 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15122 this.addItem(r.data);
15123 this.tickItems.push(r.data);
15127 getAutoCreateNativeIOS : function()
15130 cls: 'form-group' //input-group,
15135 cls : 'roo-ios-select'
15139 combobox.name = this.name;
15142 if (this.disabled) {
15143 combobox.disabled = true;
15146 var settings = this;
15148 ['xs','sm','md','lg'].map(function(size){
15149 if (settings[size]) {
15150 cfg.cls += ' col-' + size + '-' + settings[size];
15160 initIOSView : function()
15162 this.store.on('load', this.onIOSViewLoad, this);
15167 onIOSViewLoad : function()
15169 if(this.store.getCount() < 1){
15173 this.clearIOSView();
15175 if(this.allowBlank) {
15177 var default_text = '-- SELECT --';
15179 var opt = this.inputEl().createChild({
15182 html : default_text
15186 o[this.valueField] = 0;
15187 o[this.displayField] = default_text;
15189 this.ios_options.push({
15196 this.store.data.each(function(d, rowIndex){
15200 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15201 html = d.data[this.displayField];
15206 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15207 value = d.data[this.valueField];
15216 if(this.value == d.data[this.valueField]){
15217 option['selected'] = true;
15220 var opt = this.inputEl().createChild(option);
15222 this.ios_options.push({
15229 this.inputEl().on('change', function(){
15230 this.fireEvent('select', this);
15235 clearIOSView: function()
15237 this.inputEl().dom.innerHTML = '';
15239 this.ios_options = [];
15242 setIOSValue: function(v)
15246 if(!this.ios_options){
15250 Roo.each(this.ios_options, function(opts){
15252 opts.el.dom.removeAttribute('selected');
15254 if(opts.data[this.valueField] != v){
15258 opts.el.dom.setAttribute('selected', true);
15264 * @cfg {Boolean} grow
15268 * @cfg {Number} growMin
15272 * @cfg {Number} growMax
15281 Roo.apply(Roo.bootstrap.ComboBox, {
15285 cls: 'modal-header',
15307 cls: 'list-group-item',
15311 cls: 'roo-combobox-list-group-item-value'
15315 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15329 listItemCheckbox : {
15331 cls: 'list-group-item',
15335 cls: 'roo-combobox-list-group-item-value'
15339 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15355 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15360 cls: 'modal-footer',
15368 cls: 'col-xs-6 text-left',
15371 cls: 'btn btn-danger roo-touch-view-cancel',
15377 cls: 'col-xs-6 text-right',
15380 cls: 'btn btn-success roo-touch-view-ok',
15391 Roo.apply(Roo.bootstrap.ComboBox, {
15393 touchViewTemplate : {
15395 cls: 'modal fade roo-combobox-touch-view',
15399 cls: 'modal-dialog',
15400 style : 'position:fixed', // we have to fix position....
15404 cls: 'modal-content',
15406 Roo.bootstrap.ComboBox.header,
15407 Roo.bootstrap.ComboBox.body,
15408 Roo.bootstrap.ComboBox.footer
15417 * Ext JS Library 1.1.1
15418 * Copyright(c) 2006-2007, Ext JS, LLC.
15420 * Originally Released Under LGPL - original licence link has changed is not relivant.
15423 * <script type="text/javascript">
15428 * @extends Roo.util.Observable
15429 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
15430 * This class also supports single and multi selection modes. <br>
15431 * Create a data model bound view:
15433 var store = new Roo.data.Store(...);
15435 var view = new Roo.View({
15437 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
15439 singleSelect: true,
15440 selectedClass: "ydataview-selected",
15444 // listen for node click?
15445 view.on("click", function(vw, index, node, e){
15446 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15450 dataModel.load("foobar.xml");
15452 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15454 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15455 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15457 * Note: old style constructor is still suported (container, template, config)
15460 * Create a new View
15461 * @param {Object} config The config object
15464 Roo.View = function(config, depreciated_tpl, depreciated_config){
15466 this.parent = false;
15468 if (typeof(depreciated_tpl) == 'undefined') {
15469 // new way.. - universal constructor.
15470 Roo.apply(this, config);
15471 this.el = Roo.get(this.el);
15474 this.el = Roo.get(config);
15475 this.tpl = depreciated_tpl;
15476 Roo.apply(this, depreciated_config);
15478 this.wrapEl = this.el.wrap().wrap();
15479 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15482 if(typeof(this.tpl) == "string"){
15483 this.tpl = new Roo.Template(this.tpl);
15485 // support xtype ctors..
15486 this.tpl = new Roo.factory(this.tpl, Roo);
15490 this.tpl.compile();
15495 * @event beforeclick
15496 * Fires before a click is processed. Returns false to cancel the default action.
15497 * @param {Roo.View} this
15498 * @param {Number} index The index of the target node
15499 * @param {HTMLElement} node The target node
15500 * @param {Roo.EventObject} e The raw event object
15502 "beforeclick" : true,
15505 * Fires when a template node is clicked.
15506 * @param {Roo.View} this
15507 * @param {Number} index The index of the target node
15508 * @param {HTMLElement} node The target node
15509 * @param {Roo.EventObject} e The raw event object
15514 * Fires when a template node is double clicked.
15515 * @param {Roo.View} this
15516 * @param {Number} index The index of the target node
15517 * @param {HTMLElement} node The target node
15518 * @param {Roo.EventObject} e The raw event object
15522 * @event contextmenu
15523 * Fires when a template node is right clicked.
15524 * @param {Roo.View} this
15525 * @param {Number} index The index of the target node
15526 * @param {HTMLElement} node The target node
15527 * @param {Roo.EventObject} e The raw event object
15529 "contextmenu" : true,
15531 * @event selectionchange
15532 * Fires when the selected nodes change.
15533 * @param {Roo.View} this
15534 * @param {Array} selections Array of the selected nodes
15536 "selectionchange" : true,
15539 * @event beforeselect
15540 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15541 * @param {Roo.View} this
15542 * @param {HTMLElement} node The node to be selected
15543 * @param {Array} selections Array of currently selected nodes
15545 "beforeselect" : true,
15547 * @event preparedata
15548 * Fires on every row to render, to allow you to change the data.
15549 * @param {Roo.View} this
15550 * @param {Object} data to be rendered (change this)
15552 "preparedata" : true
15560 "click": this.onClick,
15561 "dblclick": this.onDblClick,
15562 "contextmenu": this.onContextMenu,
15566 this.selections = [];
15568 this.cmp = new Roo.CompositeElementLite([]);
15570 this.store = Roo.factory(this.store, Roo.data);
15571 this.setStore(this.store, true);
15574 if ( this.footer && this.footer.xtype) {
15576 var fctr = this.wrapEl.appendChild(document.createElement("div"));
15578 this.footer.dataSource = this.store;
15579 this.footer.container = fctr;
15580 this.footer = Roo.factory(this.footer, Roo);
15581 fctr.insertFirst(this.el);
15583 // this is a bit insane - as the paging toolbar seems to detach the el..
15584 // dom.parentNode.parentNode.parentNode
15585 // they get detached?
15589 Roo.View.superclass.constructor.call(this);
15594 Roo.extend(Roo.View, Roo.util.Observable, {
15597 * @cfg {Roo.data.Store} store Data store to load data from.
15602 * @cfg {String|Roo.Element} el The container element.
15607 * @cfg {String|Roo.Template} tpl The template used by this View
15611 * @cfg {String} dataName the named area of the template to use as the data area
15612 * Works with domtemplates roo-name="name"
15616 * @cfg {String} selectedClass The css class to add to selected nodes
15618 selectedClass : "x-view-selected",
15620 * @cfg {String} emptyText The empty text to show when nothing is loaded.
15625 * @cfg {String} text to display on mask (default Loading)
15629 * @cfg {Boolean} multiSelect Allow multiple selection
15631 multiSelect : false,
15633 * @cfg {Boolean} singleSelect Allow single selection
15635 singleSelect: false,
15638 * @cfg {Boolean} toggleSelect - selecting
15640 toggleSelect : false,
15643 * @cfg {Boolean} tickable - selecting
15648 * Returns the element this view is bound to.
15649 * @return {Roo.Element}
15651 getEl : function(){
15652 return this.wrapEl;
15658 * Refreshes the view. - called by datachanged on the store. - do not call directly.
15660 refresh : function(){
15661 //Roo.log('refresh');
15664 // if we are using something like 'domtemplate', then
15665 // the what gets used is:
15666 // t.applySubtemplate(NAME, data, wrapping data..)
15667 // the outer template then get' applied with
15668 // the store 'extra data'
15669 // and the body get's added to the
15670 // roo-name="data" node?
15671 // <span class='roo-tpl-{name}'></span> ?????
15675 this.clearSelections();
15676 this.el.update("");
15678 var records = this.store.getRange();
15679 if(records.length < 1) {
15681 // is this valid?? = should it render a template??
15683 this.el.update(this.emptyText);
15687 if (this.dataName) {
15688 this.el.update(t.apply(this.store.meta)); //????
15689 el = this.el.child('.roo-tpl-' + this.dataName);
15692 for(var i = 0, len = records.length; i < len; i++){
15693 var data = this.prepareData(records[i].data, i, records[i]);
15694 this.fireEvent("preparedata", this, data, i, records[i]);
15696 var d = Roo.apply({}, data);
15699 Roo.apply(d, {'roo-id' : Roo.id()});
15703 Roo.each(this.parent.item, function(item){
15704 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15707 Roo.apply(d, {'roo-data-checked' : 'checked'});
15711 html[html.length] = Roo.util.Format.trim(
15713 t.applySubtemplate(this.dataName, d, this.store.meta) :
15720 el.update(html.join(""));
15721 this.nodes = el.dom.childNodes;
15722 this.updateIndexes(0);
15727 * Function to override to reformat the data that is sent to
15728 * the template for each node.
15729 * DEPRICATED - use the preparedata event handler.
15730 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15731 * a JSON object for an UpdateManager bound view).
15733 prepareData : function(data, index, record)
15735 this.fireEvent("preparedata", this, data, index, record);
15739 onUpdate : function(ds, record){
15740 // Roo.log('on update');
15741 this.clearSelections();
15742 var index = this.store.indexOf(record);
15743 var n = this.nodes[index];
15744 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
15745 n.parentNode.removeChild(n);
15746 this.updateIndexes(index, index);
15752 onAdd : function(ds, records, index)
15754 //Roo.log(['on Add', ds, records, index] );
15755 this.clearSelections();
15756 if(this.nodes.length == 0){
15760 var n = this.nodes[index];
15761 for(var i = 0, len = records.length; i < len; i++){
15762 var d = this.prepareData(records[i].data, i, records[i]);
15764 this.tpl.insertBefore(n, d);
15767 this.tpl.append(this.el, d);
15770 this.updateIndexes(index);
15773 onRemove : function(ds, record, index){
15774 // Roo.log('onRemove');
15775 this.clearSelections();
15776 var el = this.dataName ?
15777 this.el.child('.roo-tpl-' + this.dataName) :
15780 el.dom.removeChild(this.nodes[index]);
15781 this.updateIndexes(index);
15785 * Refresh an individual node.
15786 * @param {Number} index
15788 refreshNode : function(index){
15789 this.onUpdate(this.store, this.store.getAt(index));
15792 updateIndexes : function(startIndex, endIndex){
15793 var ns = this.nodes;
15794 startIndex = startIndex || 0;
15795 endIndex = endIndex || ns.length - 1;
15796 for(var i = startIndex; i <= endIndex; i++){
15797 ns[i].nodeIndex = i;
15802 * Changes the data store this view uses and refresh the view.
15803 * @param {Store} store
15805 setStore : function(store, initial){
15806 if(!initial && this.store){
15807 this.store.un("datachanged", this.refresh);
15808 this.store.un("add", this.onAdd);
15809 this.store.un("remove", this.onRemove);
15810 this.store.un("update", this.onUpdate);
15811 this.store.un("clear", this.refresh);
15812 this.store.un("beforeload", this.onBeforeLoad);
15813 this.store.un("load", this.onLoad);
15814 this.store.un("loadexception", this.onLoad);
15818 store.on("datachanged", this.refresh, this);
15819 store.on("add", this.onAdd, this);
15820 store.on("remove", this.onRemove, this);
15821 store.on("update", this.onUpdate, this);
15822 store.on("clear", this.refresh, this);
15823 store.on("beforeload", this.onBeforeLoad, this);
15824 store.on("load", this.onLoad, this);
15825 store.on("loadexception", this.onLoad, this);
15833 * onbeforeLoad - masks the loading area.
15836 onBeforeLoad : function(store,opts)
15838 //Roo.log('onBeforeLoad');
15840 this.el.update("");
15842 this.el.mask(this.mask ? this.mask : "Loading" );
15844 onLoad : function ()
15851 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15852 * @param {HTMLElement} node
15853 * @return {HTMLElement} The template node
15855 findItemFromChild : function(node){
15856 var el = this.dataName ?
15857 this.el.child('.roo-tpl-' + this.dataName,true) :
15860 if(!node || node.parentNode == el){
15863 var p = node.parentNode;
15864 while(p && p != el){
15865 if(p.parentNode == el){
15874 onClick : function(e){
15875 var item = this.findItemFromChild(e.getTarget());
15877 var index = this.indexOf(item);
15878 if(this.onItemClick(item, index, e) !== false){
15879 this.fireEvent("click", this, index, item, e);
15882 this.clearSelections();
15887 onContextMenu : function(e){
15888 var item = this.findItemFromChild(e.getTarget());
15890 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15895 onDblClick : function(e){
15896 var item = this.findItemFromChild(e.getTarget());
15898 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15902 onItemClick : function(item, index, e)
15904 if(this.fireEvent("beforeclick", this, index, item, e) === false){
15907 if (this.toggleSelect) {
15908 var m = this.isSelected(item) ? 'unselect' : 'select';
15911 _t[m](item, true, false);
15914 if(this.multiSelect || this.singleSelect){
15915 if(this.multiSelect && e.shiftKey && this.lastSelection){
15916 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
15918 this.select(item, this.multiSelect && e.ctrlKey);
15919 this.lastSelection = item;
15922 if(!this.tickable){
15923 e.preventDefault();
15931 * Get the number of selected nodes.
15934 getSelectionCount : function(){
15935 return this.selections.length;
15939 * Get the currently selected nodes.
15940 * @return {Array} An array of HTMLElements
15942 getSelectedNodes : function(){
15943 return this.selections;
15947 * Get the indexes of the selected nodes.
15950 getSelectedIndexes : function(){
15951 var indexes = [], s = this.selections;
15952 for(var i = 0, len = s.length; i < len; i++){
15953 indexes.push(s[i].nodeIndex);
15959 * Clear all selections
15960 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
15962 clearSelections : function(suppressEvent){
15963 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
15964 this.cmp.elements = this.selections;
15965 this.cmp.removeClass(this.selectedClass);
15966 this.selections = [];
15967 if(!suppressEvent){
15968 this.fireEvent("selectionchange", this, this.selections);
15974 * Returns true if the passed node is selected
15975 * @param {HTMLElement/Number} node The node or node index
15976 * @return {Boolean}
15978 isSelected : function(node){
15979 var s = this.selections;
15983 node = this.getNode(node);
15984 return s.indexOf(node) !== -1;
15989 * @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
15990 * @param {Boolean} keepExisting (optional) true to keep existing selections
15991 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15993 select : function(nodeInfo, keepExisting, suppressEvent){
15994 if(nodeInfo instanceof Array){
15996 this.clearSelections(true);
15998 for(var i = 0, len = nodeInfo.length; i < len; i++){
15999 this.select(nodeInfo[i], true, true);
16003 var node = this.getNode(nodeInfo);
16004 if(!node || this.isSelected(node)){
16005 return; // already selected.
16008 this.clearSelections(true);
16011 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16012 Roo.fly(node).addClass(this.selectedClass);
16013 this.selections.push(node);
16014 if(!suppressEvent){
16015 this.fireEvent("selectionchange", this, this.selections);
16023 * @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
16024 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16025 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16027 unselect : function(nodeInfo, keepExisting, suppressEvent)
16029 if(nodeInfo instanceof Array){
16030 Roo.each(this.selections, function(s) {
16031 this.unselect(s, nodeInfo);
16035 var node = this.getNode(nodeInfo);
16036 if(!node || !this.isSelected(node)){
16037 //Roo.log("not selected");
16038 return; // not selected.
16042 Roo.each(this.selections, function(s) {
16044 Roo.fly(node).removeClass(this.selectedClass);
16051 this.selections= ns;
16052 this.fireEvent("selectionchange", this, this.selections);
16056 * Gets a template node.
16057 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16058 * @return {HTMLElement} The node or null if it wasn't found
16060 getNode : function(nodeInfo){
16061 if(typeof nodeInfo == "string"){
16062 return document.getElementById(nodeInfo);
16063 }else if(typeof nodeInfo == "number"){
16064 return this.nodes[nodeInfo];
16070 * Gets a range template nodes.
16071 * @param {Number} startIndex
16072 * @param {Number} endIndex
16073 * @return {Array} An array of nodes
16075 getNodes : function(start, end){
16076 var ns = this.nodes;
16077 start = start || 0;
16078 end = typeof end == "undefined" ? ns.length - 1 : end;
16081 for(var i = start; i <= end; i++){
16085 for(var i = start; i >= end; i--){
16093 * Finds the index of the passed node
16094 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16095 * @return {Number} The index of the node or -1
16097 indexOf : function(node){
16098 node = this.getNode(node);
16099 if(typeof node.nodeIndex == "number"){
16100 return node.nodeIndex;
16102 var ns = this.nodes;
16103 for(var i = 0, len = ns.length; i < len; i++){
16114 * based on jquery fullcalendar
16118 Roo.bootstrap = Roo.bootstrap || {};
16120 * @class Roo.bootstrap.Calendar
16121 * @extends Roo.bootstrap.Component
16122 * Bootstrap Calendar class
16123 * @cfg {Boolean} loadMask (true|false) default false
16124 * @cfg {Object} header generate the user specific header of the calendar, default false
16127 * Create a new Container
16128 * @param {Object} config The config object
16133 Roo.bootstrap.Calendar = function(config){
16134 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16138 * Fires when a date is selected
16139 * @param {DatePicker} this
16140 * @param {Date} date The selected date
16144 * @event monthchange
16145 * Fires when the displayed month changes
16146 * @param {DatePicker} this
16147 * @param {Date} date The selected month
16149 'monthchange': true,
16151 * @event evententer
16152 * Fires when mouse over an event
16153 * @param {Calendar} this
16154 * @param {event} Event
16156 'evententer': true,
16158 * @event eventleave
16159 * Fires when the mouse leaves an
16160 * @param {Calendar} this
16163 'eventleave': true,
16165 * @event eventclick
16166 * Fires when the mouse click an
16167 * @param {Calendar} this
16176 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
16179 * @cfg {Number} startDay
16180 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16188 getAutoCreate : function(){
16191 var fc_button = function(name, corner, style, content ) {
16192 return Roo.apply({},{
16194 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
16196 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16199 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16210 style : 'width:100%',
16217 cls : 'fc-header-left',
16219 fc_button('prev', 'left', 'arrow', '‹' ),
16220 fc_button('next', 'right', 'arrow', '›' ),
16221 { tag: 'span', cls: 'fc-header-space' },
16222 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
16230 cls : 'fc-header-center',
16234 cls: 'fc-header-title',
16237 html : 'month / year'
16245 cls : 'fc-header-right',
16247 /* fc_button('month', 'left', '', 'month' ),
16248 fc_button('week', '', '', 'week' ),
16249 fc_button('day', 'right', '', 'day' )
16261 header = this.header;
16264 var cal_heads = function() {
16266 // fixme - handle this.
16268 for (var i =0; i < Date.dayNames.length; i++) {
16269 var d = Date.dayNames[i];
16272 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16273 html : d.substring(0,3)
16277 ret[0].cls += ' fc-first';
16278 ret[6].cls += ' fc-last';
16281 var cal_cell = function(n) {
16284 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16289 cls: 'fc-day-number',
16293 cls: 'fc-day-content',
16297 style: 'position: relative;' // height: 17px;
16309 var cal_rows = function() {
16312 for (var r = 0; r < 6; r++) {
16319 for (var i =0; i < Date.dayNames.length; i++) {
16320 var d = Date.dayNames[i];
16321 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16324 row.cn[0].cls+=' fc-first';
16325 row.cn[0].cn[0].style = 'min-height:90px';
16326 row.cn[6].cls+=' fc-last';
16330 ret[0].cls += ' fc-first';
16331 ret[4].cls += ' fc-prev-last';
16332 ret[5].cls += ' fc-last';
16339 cls: 'fc-border-separate',
16340 style : 'width:100%',
16348 cls : 'fc-first fc-last',
16366 cls : 'fc-content',
16367 style : "position: relative;",
16370 cls : 'fc-view fc-view-month fc-grid',
16371 style : 'position: relative',
16372 unselectable : 'on',
16375 cls : 'fc-event-container',
16376 style : 'position:absolute;z-index:8;top:0;left:0;'
16394 initEvents : function()
16397 throw "can not find store for calendar";
16403 style: "text-align:center",
16407 style: "background-color:white;width:50%;margin:250 auto",
16411 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
16422 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16424 var size = this.el.select('.fc-content', true).first().getSize();
16425 this.maskEl.setSize(size.width, size.height);
16426 this.maskEl.enableDisplayMode("block");
16427 if(!this.loadMask){
16428 this.maskEl.hide();
16431 this.store = Roo.factory(this.store, Roo.data);
16432 this.store.on('load', this.onLoad, this);
16433 this.store.on('beforeload', this.onBeforeLoad, this);
16437 this.cells = this.el.select('.fc-day',true);
16438 //Roo.log(this.cells);
16439 this.textNodes = this.el.query('.fc-day-number');
16440 this.cells.addClassOnOver('fc-state-hover');
16442 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16443 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16444 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16445 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16447 this.on('monthchange', this.onMonthChange, this);
16449 this.update(new Date().clearTime());
16452 resize : function() {
16453 var sz = this.el.getSize();
16455 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16456 this.el.select('.fc-day-content div',true).setHeight(34);
16461 showPrevMonth : function(e){
16462 this.update(this.activeDate.add("mo", -1));
16464 showToday : function(e){
16465 this.update(new Date().clearTime());
16468 showNextMonth : function(e){
16469 this.update(this.activeDate.add("mo", 1));
16473 showPrevYear : function(){
16474 this.update(this.activeDate.add("y", -1));
16478 showNextYear : function(){
16479 this.update(this.activeDate.add("y", 1));
16484 update : function(date)
16486 var vd = this.activeDate;
16487 this.activeDate = date;
16488 // if(vd && this.el){
16489 // var t = date.getTime();
16490 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16491 // Roo.log('using add remove');
16493 // this.fireEvent('monthchange', this, date);
16495 // this.cells.removeClass("fc-state-highlight");
16496 // this.cells.each(function(c){
16497 // if(c.dateValue == t){
16498 // c.addClass("fc-state-highlight");
16499 // setTimeout(function(){
16500 // try{c.dom.firstChild.focus();}catch(e){}
16510 var days = date.getDaysInMonth();
16512 var firstOfMonth = date.getFirstDateOfMonth();
16513 var startingPos = firstOfMonth.getDay()-this.startDay;
16515 if(startingPos < this.startDay){
16519 var pm = date.add(Date.MONTH, -1);
16520 var prevStart = pm.getDaysInMonth()-startingPos;
16522 this.cells = this.el.select('.fc-day',true);
16523 this.textNodes = this.el.query('.fc-day-number');
16524 this.cells.addClassOnOver('fc-state-hover');
16526 var cells = this.cells.elements;
16527 var textEls = this.textNodes;
16529 Roo.each(cells, function(cell){
16530 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16533 days += startingPos;
16535 // convert everything to numbers so it's fast
16536 var day = 86400000;
16537 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16540 //Roo.log(prevStart);
16542 var today = new Date().clearTime().getTime();
16543 var sel = date.clearTime().getTime();
16544 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16545 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16546 var ddMatch = this.disabledDatesRE;
16547 var ddText = this.disabledDatesText;
16548 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16549 var ddaysText = this.disabledDaysText;
16550 var format = this.format;
16552 var setCellClass = function(cal, cell){
16556 //Roo.log('set Cell Class');
16558 var t = d.getTime();
16562 cell.dateValue = t;
16564 cell.className += " fc-today";
16565 cell.className += " fc-state-highlight";
16566 cell.title = cal.todayText;
16569 // disable highlight in other month..
16570 //cell.className += " fc-state-highlight";
16575 cell.className = " fc-state-disabled";
16576 cell.title = cal.minText;
16580 cell.className = " fc-state-disabled";
16581 cell.title = cal.maxText;
16585 if(ddays.indexOf(d.getDay()) != -1){
16586 cell.title = ddaysText;
16587 cell.className = " fc-state-disabled";
16590 if(ddMatch && format){
16591 var fvalue = d.dateFormat(format);
16592 if(ddMatch.test(fvalue)){
16593 cell.title = ddText.replace("%0", fvalue);
16594 cell.className = " fc-state-disabled";
16598 if (!cell.initialClassName) {
16599 cell.initialClassName = cell.dom.className;
16602 cell.dom.className = cell.initialClassName + ' ' + cell.className;
16607 for(; i < startingPos; i++) {
16608 textEls[i].innerHTML = (++prevStart);
16609 d.setDate(d.getDate()+1);
16611 cells[i].className = "fc-past fc-other-month";
16612 setCellClass(this, cells[i]);
16617 for(; i < days; i++){
16618 intDay = i - startingPos + 1;
16619 textEls[i].innerHTML = (intDay);
16620 d.setDate(d.getDate()+1);
16622 cells[i].className = ''; // "x-date-active";
16623 setCellClass(this, cells[i]);
16627 for(; i < 42; i++) {
16628 textEls[i].innerHTML = (++extraDays);
16629 d.setDate(d.getDate()+1);
16631 cells[i].className = "fc-future fc-other-month";
16632 setCellClass(this, cells[i]);
16635 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16637 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16639 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16640 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16642 if(totalRows != 6){
16643 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16644 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16647 this.fireEvent('monthchange', this, date);
16651 if(!this.internalRender){
16652 var main = this.el.dom.firstChild;
16653 var w = main.offsetWidth;
16654 this.el.setWidth(w + this.el.getBorderWidth("lr"));
16655 Roo.fly(main).setWidth(w);
16656 this.internalRender = true;
16657 // opera does not respect the auto grow header center column
16658 // then, after it gets a width opera refuses to recalculate
16659 // without a second pass
16660 if(Roo.isOpera && !this.secondPass){
16661 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16662 this.secondPass = true;
16663 this.update.defer(10, this, [date]);
16670 findCell : function(dt) {
16671 dt = dt.clearTime().getTime();
16673 this.cells.each(function(c){
16674 //Roo.log("check " +c.dateValue + '?=' + dt);
16675 if(c.dateValue == dt){
16685 findCells : function(ev) {
16686 var s = ev.start.clone().clearTime().getTime();
16688 var e= ev.end.clone().clearTime().getTime();
16691 this.cells.each(function(c){
16692 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16694 if(c.dateValue > e){
16697 if(c.dateValue < s){
16706 // findBestRow: function(cells)
16710 // for (var i =0 ; i < cells.length;i++) {
16711 // ret = Math.max(cells[i].rows || 0,ret);
16718 addItem : function(ev)
16720 // look for vertical location slot in
16721 var cells = this.findCells(ev);
16723 // ev.row = this.findBestRow(cells);
16725 // work out the location.
16729 for(var i =0; i < cells.length; i++) {
16731 cells[i].row = cells[0].row;
16734 cells[i].row = cells[i].row + 1;
16744 if (crow.start.getY() == cells[i].getY()) {
16746 crow.end = cells[i];
16763 cells[0].events.push(ev);
16765 this.calevents.push(ev);
16768 clearEvents: function() {
16770 if(!this.calevents){
16774 Roo.each(this.cells.elements, function(c){
16780 Roo.each(this.calevents, function(e) {
16781 Roo.each(e.els, function(el) {
16782 el.un('mouseenter' ,this.onEventEnter, this);
16783 el.un('mouseleave' ,this.onEventLeave, this);
16788 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16794 renderEvents: function()
16798 this.cells.each(function(c) {
16807 if(c.row != c.events.length){
16808 r = 4 - (4 - (c.row - c.events.length));
16811 c.events = ev.slice(0, r);
16812 c.more = ev.slice(r);
16814 if(c.more.length && c.more.length == 1){
16815 c.events.push(c.more.pop());
16818 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16822 this.cells.each(function(c) {
16824 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16827 for (var e = 0; e < c.events.length; e++){
16828 var ev = c.events[e];
16829 var rows = ev.rows;
16831 for(var i = 0; i < rows.length; i++) {
16833 // how many rows should it span..
16836 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16837 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16839 unselectable : "on",
16842 cls: 'fc-event-inner',
16846 // cls: 'fc-event-time',
16847 // html : cells.length > 1 ? '' : ev.time
16851 cls: 'fc-event-title',
16852 html : String.format('{0}', ev.title)
16859 cls: 'ui-resizable-handle ui-resizable-e',
16860 html : '  '
16867 cfg.cls += ' fc-event-start';
16869 if ((i+1) == rows.length) {
16870 cfg.cls += ' fc-event-end';
16873 var ctr = _this.el.select('.fc-event-container',true).first();
16874 var cg = ctr.createChild(cfg);
16876 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16877 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16879 var r = (c.more.length) ? 1 : 0;
16880 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
16881 cg.setWidth(ebox.right - sbox.x -2);
16883 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16884 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16885 cg.on('click', _this.onEventClick, _this, ev);
16896 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16897 style : 'position: absolute',
16898 unselectable : "on",
16901 cls: 'fc-event-inner',
16905 cls: 'fc-event-title',
16913 cls: 'ui-resizable-handle ui-resizable-e',
16914 html : '  '
16920 var ctr = _this.el.select('.fc-event-container',true).first();
16921 var cg = ctr.createChild(cfg);
16923 var sbox = c.select('.fc-day-content',true).first().getBox();
16924 var ebox = c.select('.fc-day-content',true).first().getBox();
16926 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
16927 cg.setWidth(ebox.right - sbox.x -2);
16929 cg.on('click', _this.onMoreEventClick, _this, c.more);
16939 onEventEnter: function (e, el,event,d) {
16940 this.fireEvent('evententer', this, el, event);
16943 onEventLeave: function (e, el,event,d) {
16944 this.fireEvent('eventleave', this, el, event);
16947 onEventClick: function (e, el,event,d) {
16948 this.fireEvent('eventclick', this, el, event);
16951 onMonthChange: function () {
16955 onMoreEventClick: function(e, el, more)
16959 this.calpopover.placement = 'right';
16960 this.calpopover.setTitle('More');
16962 this.calpopover.setContent('');
16964 var ctr = this.calpopover.el.select('.popover-content', true).first();
16966 Roo.each(more, function(m){
16968 cls : 'fc-event-hori fc-event-draggable',
16971 var cg = ctr.createChild(cfg);
16973 cg.on('click', _this.onEventClick, _this, m);
16976 this.calpopover.show(el);
16981 onLoad: function ()
16983 this.calevents = [];
16986 if(this.store.getCount() > 0){
16987 this.store.data.each(function(d){
16990 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
16991 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
16992 time : d.data.start_time,
16993 title : d.data.title,
16994 description : d.data.description,
16995 venue : d.data.venue
17000 this.renderEvents();
17002 if(this.calevents.length && this.loadMask){
17003 this.maskEl.hide();
17007 onBeforeLoad: function()
17009 this.clearEvents();
17011 this.maskEl.show();
17025 * @class Roo.bootstrap.Popover
17026 * @extends Roo.bootstrap.Component
17027 * Bootstrap Popover class
17028 * @cfg {String} html contents of the popover (or false to use children..)
17029 * @cfg {String} title of popover (or false to hide)
17030 * @cfg {String} placement how it is placed
17031 * @cfg {String} trigger click || hover (or false to trigger manually)
17032 * @cfg {String} over what (parent or false to trigger manually.)
17033 * @cfg {Number} delay - delay before showing
17036 * Create a new Popover
17037 * @param {Object} config The config object
17040 Roo.bootstrap.Popover = function(config){
17041 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17047 * After the popover show
17049 * @param {Roo.bootstrap.Popover} this
17054 * After the popover hide
17056 * @param {Roo.bootstrap.Popover} this
17062 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
17064 title: 'Fill in a title',
17067 placement : 'right',
17068 trigger : 'hover', // hover
17074 can_build_overlaid : false,
17076 getChildContainer : function()
17078 return this.el.select('.popover-content',true).first();
17081 getAutoCreate : function(){
17084 cls : 'popover roo-dynamic',
17085 style: 'display:block',
17091 cls : 'popover-inner',
17095 cls: 'popover-title',
17099 cls : 'popover-content',
17110 setTitle: function(str)
17113 this.el.select('.popover-title',true).first().dom.innerHTML = str;
17115 setContent: function(str)
17118 this.el.select('.popover-content',true).first().dom.innerHTML = str;
17120 // as it get's added to the bottom of the page.
17121 onRender : function(ct, position)
17123 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17125 var cfg = Roo.apply({}, this.getAutoCreate());
17129 cfg.cls += ' ' + this.cls;
17132 cfg.style = this.style;
17134 //Roo.log("adding to ");
17135 this.el = Roo.get(document.body).createChild(cfg, position);
17136 // Roo.log(this.el);
17141 initEvents : function()
17143 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17144 this.el.enableDisplayMode('block');
17146 if (this.over === false) {
17149 if (this.triggers === false) {
17152 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17153 var triggers = this.trigger ? this.trigger.split(' ') : [];
17154 Roo.each(triggers, function(trigger) {
17156 if (trigger == 'click') {
17157 on_el.on('click', this.toggle, this);
17158 } else if (trigger != 'manual') {
17159 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
17160 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17162 on_el.on(eventIn ,this.enter, this);
17163 on_el.on(eventOut, this.leave, this);
17174 toggle : function () {
17175 this.hoverState == 'in' ? this.leave() : this.enter();
17178 enter : function () {
17180 clearTimeout(this.timeout);
17182 this.hoverState = 'in';
17184 if (!this.delay || !this.delay.show) {
17189 this.timeout = setTimeout(function () {
17190 if (_t.hoverState == 'in') {
17193 }, this.delay.show)
17196 leave : function() {
17197 clearTimeout(this.timeout);
17199 this.hoverState = 'out';
17201 if (!this.delay || !this.delay.hide) {
17206 this.timeout = setTimeout(function () {
17207 if (_t.hoverState == 'out') {
17210 }, this.delay.hide)
17213 show : function (on_el)
17216 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17220 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17221 if (this.html !== false) {
17222 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17224 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17225 if (!this.title.length) {
17226 this.el.select('.popover-title',true).hide();
17229 var placement = typeof this.placement == 'function' ?
17230 this.placement.call(this, this.el, on_el) :
17233 var autoToken = /\s?auto?\s?/i;
17234 var autoPlace = autoToken.test(placement);
17236 placement = placement.replace(autoToken, '') || 'top';
17240 //this.el.setXY([0,0]);
17242 this.el.dom.style.display='block';
17243 this.el.addClass(placement);
17245 //this.el.appendTo(on_el);
17247 var p = this.getPosition();
17248 var box = this.el.getBox();
17253 var align = Roo.bootstrap.Popover.alignment[placement];
17254 this.el.alignTo(on_el, align[0],align[1]);
17255 //var arrow = this.el.select('.arrow',true).first();
17256 //arrow.set(align[2],
17258 this.el.addClass('in');
17261 if (this.el.hasClass('fade')) {
17265 this.hoverState = 'in';
17267 this.fireEvent('show', this);
17272 this.el.setXY([0,0]);
17273 this.el.removeClass('in');
17275 this.hoverState = null;
17277 this.fireEvent('hide', this);
17282 Roo.bootstrap.Popover.alignment = {
17283 'left' : ['r-l', [-10,0], 'right'],
17284 'right' : ['l-r', [10,0], 'left'],
17285 'bottom' : ['t-b', [0,10], 'top'],
17286 'top' : [ 'b-t', [0,-10], 'bottom']
17297 * @class Roo.bootstrap.Progress
17298 * @extends Roo.bootstrap.Component
17299 * Bootstrap Progress class
17300 * @cfg {Boolean} striped striped of the progress bar
17301 * @cfg {Boolean} active animated of the progress bar
17305 * Create a new Progress
17306 * @param {Object} config The config object
17309 Roo.bootstrap.Progress = function(config){
17310 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17313 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
17318 getAutoCreate : function(){
17326 cfg.cls += ' progress-striped';
17330 cfg.cls += ' active';
17349 * @class Roo.bootstrap.ProgressBar
17350 * @extends Roo.bootstrap.Component
17351 * Bootstrap ProgressBar class
17352 * @cfg {Number} aria_valuenow aria-value now
17353 * @cfg {Number} aria_valuemin aria-value min
17354 * @cfg {Number} aria_valuemax aria-value max
17355 * @cfg {String} label label for the progress bar
17356 * @cfg {String} panel (success | info | warning | danger )
17357 * @cfg {String} role role of the progress bar
17358 * @cfg {String} sr_only text
17362 * Create a new ProgressBar
17363 * @param {Object} config The config object
17366 Roo.bootstrap.ProgressBar = function(config){
17367 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17370 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
17374 aria_valuemax : 100,
17380 getAutoCreate : function()
17385 cls: 'progress-bar',
17386 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17398 cfg.role = this.role;
17401 if(this.aria_valuenow){
17402 cfg['aria-valuenow'] = this.aria_valuenow;
17405 if(this.aria_valuemin){
17406 cfg['aria-valuemin'] = this.aria_valuemin;
17409 if(this.aria_valuemax){
17410 cfg['aria-valuemax'] = this.aria_valuemax;
17413 if(this.label && !this.sr_only){
17414 cfg.html = this.label;
17418 cfg.cls += ' progress-bar-' + this.panel;
17424 update : function(aria_valuenow)
17426 this.aria_valuenow = aria_valuenow;
17428 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17443 * @class Roo.bootstrap.TabGroup
17444 * @extends Roo.bootstrap.Column
17445 * Bootstrap Column class
17446 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17447 * @cfg {Boolean} carousel true to make the group behave like a carousel
17448 * @cfg {Boolean} bullets show bullets for the panels
17449 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17450 * @cfg {Number} timer auto slide timer .. default 0 millisecond
17451 * @cfg {Boolean} showarrow (true|false) show arrow default true
17454 * Create a new TabGroup
17455 * @param {Object} config The config object
17458 Roo.bootstrap.TabGroup = function(config){
17459 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17461 this.navId = Roo.id();
17464 Roo.bootstrap.TabGroup.register(this);
17468 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
17471 transition : false,
17476 slideOnTouch : false,
17479 getAutoCreate : function()
17481 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17483 cfg.cls += ' tab-content';
17485 if (this.carousel) {
17486 cfg.cls += ' carousel slide';
17489 cls : 'carousel-inner',
17493 if(this.bullets && !Roo.isTouch){
17496 cls : 'carousel-bullets',
17500 if(this.bullets_cls){
17501 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17508 cfg.cn[0].cn.push(bullets);
17511 if(this.showarrow){
17512 cfg.cn[0].cn.push({
17514 class : 'carousel-arrow',
17518 class : 'carousel-prev',
17522 class : 'fa fa-chevron-left'
17528 class : 'carousel-next',
17532 class : 'fa fa-chevron-right'
17545 initEvents: function()
17547 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17548 // this.el.on("touchstart", this.onTouchStart, this);
17551 if(this.autoslide){
17554 this.slideFn = window.setInterval(function() {
17555 _this.showPanelNext();
17559 if(this.showarrow){
17560 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17561 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17567 // onTouchStart : function(e, el, o)
17569 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17573 // this.showPanelNext();
17577 getChildContainer : function()
17579 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17583 * register a Navigation item
17584 * @param {Roo.bootstrap.NavItem} the navitem to add
17586 register : function(item)
17588 this.tabs.push( item);
17589 item.navId = this.navId; // not really needed..
17594 getActivePanel : function()
17597 Roo.each(this.tabs, function(t) {
17607 getPanelByName : function(n)
17610 Roo.each(this.tabs, function(t) {
17611 if (t.tabId == n) {
17619 indexOfPanel : function(p)
17622 Roo.each(this.tabs, function(t,i) {
17623 if (t.tabId == p.tabId) {
17632 * show a specific panel
17633 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17634 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17636 showPanel : function (pan)
17638 if(this.transition || typeof(pan) == 'undefined'){
17639 Roo.log("waiting for the transitionend");
17643 if (typeof(pan) == 'number') {
17644 pan = this.tabs[pan];
17647 if (typeof(pan) == 'string') {
17648 pan = this.getPanelByName(pan);
17651 var cur = this.getActivePanel();
17654 Roo.log('pan or acitve pan is undefined');
17658 if (pan.tabId == this.getActivePanel().tabId) {
17662 if (false === cur.fireEvent('beforedeactivate')) {
17666 if(this.bullets > 0 && !Roo.isTouch){
17667 this.setActiveBullet(this.indexOfPanel(pan));
17670 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17672 this.transition = true;
17673 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
17674 var lr = dir == 'next' ? 'left' : 'right';
17675 pan.el.addClass(dir); // or prev
17676 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17677 cur.el.addClass(lr); // or right
17678 pan.el.addClass(lr);
17681 cur.el.on('transitionend', function() {
17682 Roo.log("trans end?");
17684 pan.el.removeClass([lr,dir]);
17685 pan.setActive(true);
17687 cur.el.removeClass([lr]);
17688 cur.setActive(false);
17690 _this.transition = false;
17692 }, this, { single: true } );
17697 cur.setActive(false);
17698 pan.setActive(true);
17703 showPanelNext : function()
17705 var i = this.indexOfPanel(this.getActivePanel());
17707 if (i >= this.tabs.length - 1 && !this.autoslide) {
17711 if (i >= this.tabs.length - 1 && this.autoslide) {
17715 this.showPanel(this.tabs[i+1]);
17718 showPanelPrev : function()
17720 var i = this.indexOfPanel(this.getActivePanel());
17722 if (i < 1 && !this.autoslide) {
17726 if (i < 1 && this.autoslide) {
17727 i = this.tabs.length;
17730 this.showPanel(this.tabs[i-1]);
17734 addBullet: function()
17736 if(!this.bullets || Roo.isTouch){
17739 var ctr = this.el.select('.carousel-bullets',true).first();
17740 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
17741 var bullet = ctr.createChild({
17742 cls : 'bullet bullet-' + i
17743 },ctr.dom.lastChild);
17748 bullet.on('click', (function(e, el, o, ii, t){
17750 e.preventDefault();
17752 this.showPanel(ii);
17754 if(this.autoslide && this.slideFn){
17755 clearInterval(this.slideFn);
17756 this.slideFn = window.setInterval(function() {
17757 _this.showPanelNext();
17761 }).createDelegate(this, [i, bullet], true));
17766 setActiveBullet : function(i)
17772 Roo.each(this.el.select('.bullet', true).elements, function(el){
17773 el.removeClass('selected');
17776 var bullet = this.el.select('.bullet-' + i, true).first();
17782 bullet.addClass('selected');
17793 Roo.apply(Roo.bootstrap.TabGroup, {
17797 * register a Navigation Group
17798 * @param {Roo.bootstrap.NavGroup} the navgroup to add
17800 register : function(navgrp)
17802 this.groups[navgrp.navId] = navgrp;
17806 * fetch a Navigation Group based on the navigation ID
17807 * if one does not exist , it will get created.
17808 * @param {string} the navgroup to add
17809 * @returns {Roo.bootstrap.NavGroup} the navgroup
17811 get: function(navId) {
17812 if (typeof(this.groups[navId]) == 'undefined') {
17813 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17815 return this.groups[navId] ;
17830 * @class Roo.bootstrap.TabPanel
17831 * @extends Roo.bootstrap.Component
17832 * Bootstrap TabPanel class
17833 * @cfg {Boolean} active panel active
17834 * @cfg {String} html panel content
17835 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17836 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17837 * @cfg {String} href click to link..
17841 * Create a new TabPanel
17842 * @param {Object} config The config object
17845 Roo.bootstrap.TabPanel = function(config){
17846 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17850 * Fires when the active status changes
17851 * @param {Roo.bootstrap.TabPanel} this
17852 * @param {Boolean} state the new state
17857 * @event beforedeactivate
17858 * Fires before a tab is de-activated - can be used to do validation on a form.
17859 * @param {Roo.bootstrap.TabPanel} this
17860 * @return {Boolean} false if there is an error
17863 'beforedeactivate': true
17866 this.tabId = this.tabId || Roo.id();
17870 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
17878 getAutoCreate : function(){
17881 // item is needed for carousel - not sure if it has any effect otherwise
17882 cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17883 html: this.html || ''
17887 cfg.cls += ' active';
17891 cfg.tabId = this.tabId;
17898 initEvents: function()
17900 var p = this.parent();
17902 this.navId = this.navId || p.navId;
17904 if (typeof(this.navId) != 'undefined') {
17905 // not really needed.. but just in case.. parent should be a NavGroup.
17906 var tg = Roo.bootstrap.TabGroup.get(this.navId);
17910 var i = tg.tabs.length - 1;
17912 if(this.active && tg.bullets > 0 && i < tg.bullets){
17913 tg.setActiveBullet(i);
17917 this.el.on('click', this.onClick, this);
17920 this.el.on("touchstart", this.onTouchStart, this);
17921 this.el.on("touchmove", this.onTouchMove, this);
17922 this.el.on("touchend", this.onTouchEnd, this);
17927 onRender : function(ct, position)
17929 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
17932 setActive : function(state)
17934 Roo.log("panel - set active " + this.tabId + "=" + state);
17936 this.active = state;
17938 this.el.removeClass('active');
17940 } else if (!this.el.hasClass('active')) {
17941 this.el.addClass('active');
17944 this.fireEvent('changed', this, state);
17947 onClick : function(e)
17949 e.preventDefault();
17951 if(!this.href.length){
17955 window.location.href = this.href;
17964 onTouchStart : function(e)
17966 this.swiping = false;
17968 this.startX = e.browserEvent.touches[0].clientX;
17969 this.startY = e.browserEvent.touches[0].clientY;
17972 onTouchMove : function(e)
17974 this.swiping = true;
17976 this.endX = e.browserEvent.touches[0].clientX;
17977 this.endY = e.browserEvent.touches[0].clientY;
17980 onTouchEnd : function(e)
17987 var tabGroup = this.parent();
17989 if(this.endX > this.startX){ // swiping right
17990 tabGroup.showPanelPrev();
17994 if(this.startX > this.endX){ // swiping left
17995 tabGroup.showPanelNext();
18014 * @class Roo.bootstrap.DateField
18015 * @extends Roo.bootstrap.Input
18016 * Bootstrap DateField class
18017 * @cfg {Number} weekStart default 0
18018 * @cfg {String} viewMode default empty, (months|years)
18019 * @cfg {String} minViewMode default empty, (months|years)
18020 * @cfg {Number} startDate default -Infinity
18021 * @cfg {Number} endDate default Infinity
18022 * @cfg {Boolean} todayHighlight default false
18023 * @cfg {Boolean} todayBtn default false
18024 * @cfg {Boolean} calendarWeeks default false
18025 * @cfg {Object} daysOfWeekDisabled default empty
18026 * @cfg {Boolean} singleMode default false (true | false)
18028 * @cfg {Boolean} keyboardNavigation default true
18029 * @cfg {String} language default en
18032 * Create a new DateField
18033 * @param {Object} config The config object
18036 Roo.bootstrap.DateField = function(config){
18037 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18041 * Fires when this field show.
18042 * @param {Roo.bootstrap.DateField} this
18043 * @param {Mixed} date The date value
18048 * Fires when this field hide.
18049 * @param {Roo.bootstrap.DateField} this
18050 * @param {Mixed} date The date value
18055 * Fires when select a date.
18056 * @param {Roo.bootstrap.DateField} this
18057 * @param {Mixed} date The date value
18061 * @event beforeselect
18062 * Fires when before select a date.
18063 * @param {Roo.bootstrap.DateField} this
18064 * @param {Mixed} date The date value
18066 beforeselect : true
18070 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
18073 * @cfg {String} format
18074 * The default date format string which can be overriden for localization support. The format must be
18075 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18079 * @cfg {String} altFormats
18080 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18081 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18083 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18091 todayHighlight : false,
18097 keyboardNavigation: true,
18099 calendarWeeks: false,
18101 startDate: -Infinity,
18105 daysOfWeekDisabled: [],
18109 singleMode : false,
18111 UTCDate: function()
18113 return new Date(Date.UTC.apply(Date, arguments));
18116 UTCToday: function()
18118 var today = new Date();
18119 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18122 getDate: function() {
18123 var d = this.getUTCDate();
18124 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18127 getUTCDate: function() {
18131 setDate: function(d) {
18132 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18135 setUTCDate: function(d) {
18137 this.setValue(this.formatDate(this.date));
18140 onRender: function(ct, position)
18143 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18145 this.language = this.language || 'en';
18146 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18147 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18149 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18150 this.format = this.format || 'm/d/y';
18151 this.isInline = false;
18152 this.isInput = true;
18153 this.component = this.el.select('.add-on', true).first() || false;
18154 this.component = (this.component && this.component.length === 0) ? false : this.component;
18155 this.hasInput = this.component && this.inputEl().length;
18157 if (typeof(this.minViewMode === 'string')) {
18158 switch (this.minViewMode) {
18160 this.minViewMode = 1;
18163 this.minViewMode = 2;
18166 this.minViewMode = 0;
18171 if (typeof(this.viewMode === 'string')) {
18172 switch (this.viewMode) {
18185 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18187 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18189 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18191 this.picker().on('mousedown', this.onMousedown, this);
18192 this.picker().on('click', this.onClick, this);
18194 this.picker().addClass('datepicker-dropdown');
18196 this.startViewMode = this.viewMode;
18198 if(this.singleMode){
18199 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18200 v.setVisibilityMode(Roo.Element.DISPLAY);
18204 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18205 v.setStyle('width', '189px');
18209 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18210 if(!this.calendarWeeks){
18215 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18216 v.attr('colspan', function(i, val){
18217 return parseInt(val) + 1;
18222 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18224 this.setStartDate(this.startDate);
18225 this.setEndDate(this.endDate);
18227 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18234 if(this.isInline) {
18239 picker : function()
18241 return this.pickerEl;
18242 // return this.el.select('.datepicker', true).first();
18245 fillDow: function()
18247 var dowCnt = this.weekStart;
18256 if(this.calendarWeeks){
18264 while (dowCnt < this.weekStart + 7) {
18268 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18272 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18275 fillMonths: function()
18278 var months = this.picker().select('>.datepicker-months td', true).first();
18280 months.dom.innerHTML = '';
18286 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18289 months.createChild(month);
18296 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;
18298 if (this.date < this.startDate) {
18299 this.viewDate = new Date(this.startDate);
18300 } else if (this.date > this.endDate) {
18301 this.viewDate = new Date(this.endDate);
18303 this.viewDate = new Date(this.date);
18311 var d = new Date(this.viewDate),
18312 year = d.getUTCFullYear(),
18313 month = d.getUTCMonth(),
18314 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18315 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18316 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18317 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18318 currentDate = this.date && this.date.valueOf(),
18319 today = this.UTCToday();
18321 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18323 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18325 // this.picker.select('>tfoot th.today').
18326 // .text(dates[this.language].today)
18327 // .toggle(this.todayBtn !== false);
18329 this.updateNavArrows();
18332 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18334 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18336 prevMonth.setUTCDate(day);
18338 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18340 var nextMonth = new Date(prevMonth);
18342 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18344 nextMonth = nextMonth.valueOf();
18346 var fillMonths = false;
18348 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18350 while(prevMonth.valueOf() < nextMonth) {
18353 if (prevMonth.getUTCDay() === this.weekStart) {
18355 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18363 if(this.calendarWeeks){
18364 // ISO 8601: First week contains first thursday.
18365 // ISO also states week starts on Monday, but we can be more abstract here.
18367 // Start of current week: based on weekstart/current date
18368 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18369 // Thursday of this week
18370 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18371 // First Thursday of year, year from thursday
18372 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18373 // Calendar week: ms between thursdays, div ms per day, div 7 days
18374 calWeek = (th - yth) / 864e5 / 7 + 1;
18376 fillMonths.cn.push({
18384 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18386 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18389 if (this.todayHighlight &&
18390 prevMonth.getUTCFullYear() == today.getFullYear() &&
18391 prevMonth.getUTCMonth() == today.getMonth() &&
18392 prevMonth.getUTCDate() == today.getDate()) {
18393 clsName += ' today';
18396 if (currentDate && prevMonth.valueOf() === currentDate) {
18397 clsName += ' active';
18400 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18401 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18402 clsName += ' disabled';
18405 fillMonths.cn.push({
18407 cls: 'day ' + clsName,
18408 html: prevMonth.getDate()
18411 prevMonth.setDate(prevMonth.getDate()+1);
18414 var currentYear = this.date && this.date.getUTCFullYear();
18415 var currentMonth = this.date && this.date.getUTCMonth();
18417 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18419 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18420 v.removeClass('active');
18422 if(currentYear === year && k === currentMonth){
18423 v.addClass('active');
18426 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18427 v.addClass('disabled');
18433 year = parseInt(year/10, 10) * 10;
18435 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18437 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18440 for (var i = -1; i < 11; i++) {
18441 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18443 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18451 showMode: function(dir)
18454 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18457 Roo.each(this.picker().select('>div',true).elements, function(v){
18458 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18461 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18466 if(this.isInline) {
18470 this.picker().removeClass(['bottom', 'top']);
18472 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18474 * place to the top of element!
18478 this.picker().addClass('top');
18479 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18484 this.picker().addClass('bottom');
18486 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18489 parseDate : function(value)
18491 if(!value || value instanceof Date){
18494 var v = Date.parseDate(value, this.format);
18495 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18496 v = Date.parseDate(value, 'Y-m-d');
18498 if(!v && this.altFormats){
18499 if(!this.altFormatsArray){
18500 this.altFormatsArray = this.altFormats.split("|");
18502 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18503 v = Date.parseDate(value, this.altFormatsArray[i]);
18509 formatDate : function(date, fmt)
18511 return (!date || !(date instanceof Date)) ?
18512 date : date.dateFormat(fmt || this.format);
18515 onFocus : function()
18517 Roo.bootstrap.DateField.superclass.onFocus.call(this);
18521 onBlur : function()
18523 Roo.bootstrap.DateField.superclass.onBlur.call(this);
18525 var d = this.inputEl().getValue();
18534 this.picker().show();
18538 this.fireEvent('show', this, this.date);
18543 if(this.isInline) {
18546 this.picker().hide();
18547 this.viewMode = this.startViewMode;
18550 this.fireEvent('hide', this, this.date);
18554 onMousedown: function(e)
18556 e.stopPropagation();
18557 e.preventDefault();
18562 Roo.bootstrap.DateField.superclass.keyup.call(this);
18566 setValue: function(v)
18568 if(this.fireEvent('beforeselect', this, v) !== false){
18569 var d = new Date(this.parseDate(v) ).clearTime();
18571 if(isNaN(d.getTime())){
18572 this.date = this.viewDate = '';
18573 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18577 v = this.formatDate(d);
18579 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18581 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18585 this.fireEvent('select', this, this.date);
18589 getValue: function()
18591 return this.formatDate(this.date);
18594 fireKey: function(e)
18596 if (!this.picker().isVisible()){
18597 if (e.keyCode == 27) { // allow escape to hide and re-show picker
18603 var dateChanged = false,
18605 newDate, newViewDate;
18610 e.preventDefault();
18614 if (!this.keyboardNavigation) {
18617 dir = e.keyCode == 37 ? -1 : 1;
18620 newDate = this.moveYear(this.date, dir);
18621 newViewDate = this.moveYear(this.viewDate, dir);
18622 } else if (e.shiftKey){
18623 newDate = this.moveMonth(this.date, dir);
18624 newViewDate = this.moveMonth(this.viewDate, dir);
18626 newDate = new Date(this.date);
18627 newDate.setUTCDate(this.date.getUTCDate() + dir);
18628 newViewDate = new Date(this.viewDate);
18629 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18631 if (this.dateWithinRange(newDate)){
18632 this.date = newDate;
18633 this.viewDate = newViewDate;
18634 this.setValue(this.formatDate(this.date));
18636 e.preventDefault();
18637 dateChanged = true;
18642 if (!this.keyboardNavigation) {
18645 dir = e.keyCode == 38 ? -1 : 1;
18647 newDate = this.moveYear(this.date, dir);
18648 newViewDate = this.moveYear(this.viewDate, dir);
18649 } else if (e.shiftKey){
18650 newDate = this.moveMonth(this.date, dir);
18651 newViewDate = this.moveMonth(this.viewDate, dir);
18653 newDate = new Date(this.date);
18654 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18655 newViewDate = new Date(this.viewDate);
18656 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18658 if (this.dateWithinRange(newDate)){
18659 this.date = newDate;
18660 this.viewDate = newViewDate;
18661 this.setValue(this.formatDate(this.date));
18663 e.preventDefault();
18664 dateChanged = true;
18668 this.setValue(this.formatDate(this.date));
18670 e.preventDefault();
18673 this.setValue(this.formatDate(this.date));
18687 onClick: function(e)
18689 e.stopPropagation();
18690 e.preventDefault();
18692 var target = e.getTarget();
18694 if(target.nodeName.toLowerCase() === 'i'){
18695 target = Roo.get(target).dom.parentNode;
18698 var nodeName = target.nodeName;
18699 var className = target.className;
18700 var html = target.innerHTML;
18701 //Roo.log(nodeName);
18703 switch(nodeName.toLowerCase()) {
18705 switch(className) {
18711 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18712 switch(this.viewMode){
18714 this.viewDate = this.moveMonth(this.viewDate, dir);
18718 this.viewDate = this.moveYear(this.viewDate, dir);
18724 var date = new Date();
18725 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18727 this.setValue(this.formatDate(this.date));
18734 if (className.indexOf('disabled') < 0) {
18735 this.viewDate.setUTCDate(1);
18736 if (className.indexOf('month') > -1) {
18737 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18739 var year = parseInt(html, 10) || 0;
18740 this.viewDate.setUTCFullYear(year);
18744 if(this.singleMode){
18745 this.setValue(this.formatDate(this.viewDate));
18756 //Roo.log(className);
18757 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
18758 var day = parseInt(html, 10) || 1;
18759 var year = this.viewDate.getUTCFullYear(),
18760 month = this.viewDate.getUTCMonth();
18762 if (className.indexOf('old') > -1) {
18769 } else if (className.indexOf('new') > -1) {
18777 //Roo.log([year,month,day]);
18778 this.date = this.UTCDate(year, month, day,0,0,0,0);
18779 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
18781 //Roo.log(this.formatDate(this.date));
18782 this.setValue(this.formatDate(this.date));
18789 setStartDate: function(startDate)
18791 this.startDate = startDate || -Infinity;
18792 if (this.startDate !== -Infinity) {
18793 this.startDate = this.parseDate(this.startDate);
18796 this.updateNavArrows();
18799 setEndDate: function(endDate)
18801 this.endDate = endDate || Infinity;
18802 if (this.endDate !== Infinity) {
18803 this.endDate = this.parseDate(this.endDate);
18806 this.updateNavArrows();
18809 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18811 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18812 if (typeof(this.daysOfWeekDisabled) !== 'object') {
18813 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18815 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18816 return parseInt(d, 10);
18819 this.updateNavArrows();
18822 updateNavArrows: function()
18824 if(this.singleMode){
18828 var d = new Date(this.viewDate),
18829 year = d.getUTCFullYear(),
18830 month = d.getUTCMonth();
18832 Roo.each(this.picker().select('.prev', true).elements, function(v){
18834 switch (this.viewMode) {
18837 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18843 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18850 Roo.each(this.picker().select('.next', true).elements, function(v){
18852 switch (this.viewMode) {
18855 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18861 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18869 moveMonth: function(date, dir)
18874 var new_date = new Date(date.valueOf()),
18875 day = new_date.getUTCDate(),
18876 month = new_date.getUTCMonth(),
18877 mag = Math.abs(dir),
18879 dir = dir > 0 ? 1 : -1;
18882 // If going back one month, make sure month is not current month
18883 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18885 return new_date.getUTCMonth() == month;
18887 // If going forward one month, make sure month is as expected
18888 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18890 return new_date.getUTCMonth() != new_month;
18892 new_month = month + dir;
18893 new_date.setUTCMonth(new_month);
18894 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18895 if (new_month < 0 || new_month > 11) {
18896 new_month = (new_month + 12) % 12;
18899 // For magnitudes >1, move one month at a time...
18900 for (var i=0; i<mag; i++) {
18901 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18902 new_date = this.moveMonth(new_date, dir);
18904 // ...then reset the day, keeping it in the new month
18905 new_month = new_date.getUTCMonth();
18906 new_date.setUTCDate(day);
18908 return new_month != new_date.getUTCMonth();
18911 // Common date-resetting loop -- if date is beyond end of month, make it
18914 new_date.setUTCDate(--day);
18915 new_date.setUTCMonth(new_month);
18920 moveYear: function(date, dir)
18922 return this.moveMonth(date, dir*12);
18925 dateWithinRange: function(date)
18927 return date >= this.startDate && date <= this.endDate;
18933 this.picker().remove();
18936 validateValue : function(value)
18938 if(value.length < 1) {
18939 if(this.allowBlank){
18945 if(value.length < this.minLength){
18948 if(value.length > this.maxLength){
18952 var vt = Roo.form.VTypes;
18953 if(!vt[this.vtype](value, this)){
18957 if(typeof this.validator == "function"){
18958 var msg = this.validator(value);
18964 if(this.regex && !this.regex.test(value)){
18968 if(typeof(this.parseDate(value)) == 'undefined'){
18972 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
18976 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
18986 Roo.apply(Roo.bootstrap.DateField, {
18997 html: '<i class="fa fa-arrow-left"/>'
19007 html: '<i class="fa fa-arrow-right"/>'
19049 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19050 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19051 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19052 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19053 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19066 navFnc: 'FullYear',
19071 navFnc: 'FullYear',
19076 Roo.apply(Roo.bootstrap.DateField, {
19080 cls: 'datepicker dropdown-menu roo-dynamic',
19084 cls: 'datepicker-days',
19088 cls: 'table-condensed',
19090 Roo.bootstrap.DateField.head,
19094 Roo.bootstrap.DateField.footer
19101 cls: 'datepicker-months',
19105 cls: 'table-condensed',
19107 Roo.bootstrap.DateField.head,
19108 Roo.bootstrap.DateField.content,
19109 Roo.bootstrap.DateField.footer
19116 cls: 'datepicker-years',
19120 cls: 'table-condensed',
19122 Roo.bootstrap.DateField.head,
19123 Roo.bootstrap.DateField.content,
19124 Roo.bootstrap.DateField.footer
19143 * @class Roo.bootstrap.TimeField
19144 * @extends Roo.bootstrap.Input
19145 * Bootstrap DateField class
19149 * Create a new TimeField
19150 * @param {Object} config The config object
19153 Roo.bootstrap.TimeField = function(config){
19154 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19158 * Fires when this field show.
19159 * @param {Roo.bootstrap.DateField} thisthis
19160 * @param {Mixed} date The date value
19165 * Fires when this field hide.
19166 * @param {Roo.bootstrap.DateField} this
19167 * @param {Mixed} date The date value
19172 * Fires when select a date.
19173 * @param {Roo.bootstrap.DateField} this
19174 * @param {Mixed} date The date value
19180 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
19183 * @cfg {String} format
19184 * The default time format string which can be overriden for localization support. The format must be
19185 * valid according to {@link Date#parseDate} (defaults to 'H:i').
19189 onRender: function(ct, position)
19192 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19194 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19196 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19198 this.pop = this.picker().select('>.datepicker-time',true).first();
19199 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19201 this.picker().on('mousedown', this.onMousedown, this);
19202 this.picker().on('click', this.onClick, this);
19204 this.picker().addClass('datepicker-dropdown');
19209 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19210 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19211 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19212 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19213 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19214 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19218 fireKey: function(e){
19219 if (!this.picker().isVisible()){
19220 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19226 e.preventDefault();
19234 this.onTogglePeriod();
19237 this.onIncrementMinutes();
19240 this.onDecrementMinutes();
19249 onClick: function(e) {
19250 e.stopPropagation();
19251 e.preventDefault();
19254 picker : function()
19256 return this.el.select('.datepicker', true).first();
19259 fillTime: function()
19261 var time = this.pop.select('tbody', true).first();
19263 time.dom.innerHTML = '';
19278 cls: 'hours-up glyphicon glyphicon-chevron-up'
19298 cls: 'minutes-up glyphicon glyphicon-chevron-up'
19319 cls: 'timepicker-hour',
19334 cls: 'timepicker-minute',
19349 cls: 'btn btn-primary period',
19371 cls: 'hours-down glyphicon glyphicon-chevron-down'
19391 cls: 'minutes-down glyphicon glyphicon-chevron-down'
19409 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19416 var hours = this.time.getHours();
19417 var minutes = this.time.getMinutes();
19430 hours = hours - 12;
19434 hours = '0' + hours;
19438 minutes = '0' + minutes;
19441 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19442 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19443 this.pop.select('button', true).first().dom.innerHTML = period;
19449 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19451 var cls = ['bottom'];
19453 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19460 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19465 this.picker().addClass(cls.join('-'));
19469 Roo.each(cls, function(c){
19471 _this.picker().setTop(_this.inputEl().getHeight());
19475 _this.picker().setTop(0 - _this.picker().getHeight());
19480 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19484 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19491 onFocus : function()
19493 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19497 onBlur : function()
19499 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19505 this.picker().show();
19510 this.fireEvent('show', this, this.date);
19515 this.picker().hide();
19518 this.fireEvent('hide', this, this.date);
19521 setTime : function()
19524 this.setValue(this.time.format(this.format));
19526 this.fireEvent('select', this, this.date);
19531 onMousedown: function(e){
19532 e.stopPropagation();
19533 e.preventDefault();
19536 onIncrementHours: function()
19538 Roo.log('onIncrementHours');
19539 this.time = this.time.add(Date.HOUR, 1);
19544 onDecrementHours: function()
19546 Roo.log('onDecrementHours');
19547 this.time = this.time.add(Date.HOUR, -1);
19551 onIncrementMinutes: function()
19553 Roo.log('onIncrementMinutes');
19554 this.time = this.time.add(Date.MINUTE, 1);
19558 onDecrementMinutes: function()
19560 Roo.log('onDecrementMinutes');
19561 this.time = this.time.add(Date.MINUTE, -1);
19565 onTogglePeriod: function()
19567 Roo.log('onTogglePeriod');
19568 this.time = this.time.add(Date.HOUR, 12);
19575 Roo.apply(Roo.bootstrap.TimeField, {
19605 cls: 'btn btn-info ok',
19617 Roo.apply(Roo.bootstrap.TimeField, {
19621 cls: 'datepicker dropdown-menu',
19625 cls: 'datepicker-time',
19629 cls: 'table-condensed',
19631 Roo.bootstrap.TimeField.content,
19632 Roo.bootstrap.TimeField.footer
19651 * @class Roo.bootstrap.MonthField
19652 * @extends Roo.bootstrap.Input
19653 * Bootstrap MonthField class
19655 * @cfg {String} language default en
19658 * Create a new MonthField
19659 * @param {Object} config The config object
19662 Roo.bootstrap.MonthField = function(config){
19663 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19668 * Fires when this field show.
19669 * @param {Roo.bootstrap.MonthField} this
19670 * @param {Mixed} date The date value
19675 * Fires when this field hide.
19676 * @param {Roo.bootstrap.MonthField} this
19677 * @param {Mixed} date The date value
19682 * Fires when select a date.
19683 * @param {Roo.bootstrap.MonthField} this
19684 * @param {String} oldvalue The old value
19685 * @param {String} newvalue The new value
19691 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
19693 onRender: function(ct, position)
19696 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19698 this.language = this.language || 'en';
19699 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19700 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19702 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19703 this.isInline = false;
19704 this.isInput = true;
19705 this.component = this.el.select('.add-on', true).first() || false;
19706 this.component = (this.component && this.component.length === 0) ? false : this.component;
19707 this.hasInput = this.component && this.inputEL().length;
19709 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19711 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19713 this.picker().on('mousedown', this.onMousedown, this);
19714 this.picker().on('click', this.onClick, this);
19716 this.picker().addClass('datepicker-dropdown');
19718 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19719 v.setStyle('width', '189px');
19726 if(this.isInline) {
19732 setValue: function(v, suppressEvent)
19734 var o = this.getValue();
19736 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
19740 if(suppressEvent !== true){
19741 this.fireEvent('select', this, o, v);
19746 getValue: function()
19751 onClick: function(e)
19753 e.stopPropagation();
19754 e.preventDefault();
19756 var target = e.getTarget();
19758 if(target.nodeName.toLowerCase() === 'i'){
19759 target = Roo.get(target).dom.parentNode;
19762 var nodeName = target.nodeName;
19763 var className = target.className;
19764 var html = target.innerHTML;
19766 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
19770 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
19772 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19778 picker : function()
19780 return this.pickerEl;
19783 fillMonths: function()
19786 var months = this.picker().select('>.datepicker-months td', true).first();
19788 months.dom.innerHTML = '';
19794 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
19797 months.createChild(month);
19806 if(typeof(this.vIndex) == 'undefined' && this.value.length){
19807 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19810 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19811 e.removeClass('active');
19813 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19814 e.addClass('active');
19821 if(this.isInline) {
19825 this.picker().removeClass(['bottom', 'top']);
19827 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19829 * place to the top of element!
19833 this.picker().addClass('top');
19834 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19839 this.picker().addClass('bottom');
19841 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19844 onFocus : function()
19846 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19850 onBlur : function()
19852 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19854 var d = this.inputEl().getValue();
19863 this.picker().show();
19864 this.picker().select('>.datepicker-months', true).first().show();
19868 this.fireEvent('show', this, this.date);
19873 if(this.isInline) {
19876 this.picker().hide();
19877 this.fireEvent('hide', this, this.date);
19881 onMousedown: function(e)
19883 e.stopPropagation();
19884 e.preventDefault();
19889 Roo.bootstrap.MonthField.superclass.keyup.call(this);
19893 fireKey: function(e)
19895 if (!this.picker().isVisible()){
19896 if (e.keyCode == 27) {// allow escape to hide and re-show picker
19907 e.preventDefault();
19911 dir = e.keyCode == 37 ? -1 : 1;
19913 this.vIndex = this.vIndex + dir;
19915 if(this.vIndex < 0){
19919 if(this.vIndex > 11){
19923 if(isNaN(this.vIndex)){
19927 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19933 dir = e.keyCode == 38 ? -1 : 1;
19935 this.vIndex = this.vIndex + dir * 4;
19937 if(this.vIndex < 0){
19941 if(this.vIndex > 11){
19945 if(isNaN(this.vIndex)){
19949 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19954 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19955 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19959 e.preventDefault();
19962 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19963 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19979 this.picker().remove();
19984 Roo.apply(Roo.bootstrap.MonthField, {
20003 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20004 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20009 Roo.apply(Roo.bootstrap.MonthField, {
20013 cls: 'datepicker dropdown-menu roo-dynamic',
20017 cls: 'datepicker-months',
20021 cls: 'table-condensed',
20023 Roo.bootstrap.DateField.content
20043 * @class Roo.bootstrap.CheckBox
20044 * @extends Roo.bootstrap.Input
20045 * Bootstrap CheckBox class
20047 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20048 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20049 * @cfg {String} boxLabel The text that appears beside the checkbox
20050 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20051 * @cfg {Boolean} checked initnal the element
20052 * @cfg {Boolean} inline inline the element (default false)
20053 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20056 * Create a new CheckBox
20057 * @param {Object} config The config object
20060 Roo.bootstrap.CheckBox = function(config){
20061 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20066 * Fires when the element is checked or unchecked.
20067 * @param {Roo.bootstrap.CheckBox} this This input
20068 * @param {Boolean} checked The new checked value
20075 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
20077 inputType: 'checkbox',
20085 getAutoCreate : function()
20087 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20093 cfg.cls = 'form-group ' + this.inputType; //input-group
20096 cfg.cls += ' ' + this.inputType + '-inline';
20102 type : this.inputType,
20103 value : this.inputValue,
20104 cls : 'roo-' + this.inputType, //'form-box',
20105 placeholder : this.placeholder || ''
20109 if(this.inputType != 'radio'){
20113 cls : 'roo-hidden-value',
20114 value : this.checked ? this.valueOff : this.inputValue
20119 if (this.weight) { // Validity check?
20120 cfg.cls += " " + this.inputType + "-" + this.weight;
20123 if (this.disabled) {
20124 input.disabled=true;
20128 input.checked = this.checked;
20135 input.name = this.name;
20137 if(this.inputType != 'radio'){
20138 hidden.name = this.name;
20139 input.name = '_hidden_' + this.name;
20144 input.cls += ' input-' + this.size;
20149 ['xs','sm','md','lg'].map(function(size){
20150 if (settings[size]) {
20151 cfg.cls += ' col-' + size + '-' + settings[size];
20155 var inputblock = input;
20157 if (this.before || this.after) {
20160 cls : 'input-group',
20165 inputblock.cn.push({
20167 cls : 'input-group-addon',
20172 inputblock.cn.push(input);
20174 if(this.inputType != 'radio'){
20175 inputblock.cn.push(hidden);
20179 inputblock.cn.push({
20181 cls : 'input-group-addon',
20188 if (align ==='left' && this.fieldLabel.length) {
20189 // Roo.log("left and has label");
20194 cls : 'control-label',
20195 html : this.fieldLabel
20206 if(this.labelWidth > 12){
20207 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20210 if(this.labelWidth < 13 && this.labelmd == 0){
20211 this.labelmd = this.labelWidth;
20214 if(this.labellg > 0){
20215 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20216 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20219 if(this.labelmd > 0){
20220 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20221 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20224 if(this.labelsm > 0){
20225 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20226 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20229 if(this.labelxs > 0){
20230 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20231 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20234 } else if ( this.fieldLabel.length) {
20235 // Roo.log(" label");
20239 tag: this.boxLabel ? 'span' : 'label',
20241 cls: 'control-label box-input-label',
20242 //cls : 'input-group-addon',
20243 html : this.fieldLabel
20253 // Roo.log(" no label && no align");
20254 cfg.cn = [ inputblock ] ;
20260 var boxLabelCfg = {
20262 //'for': id, // box label is handled by onclick - so no for...
20264 html: this.boxLabel
20268 boxLabelCfg.tooltip = this.tooltip;
20271 cfg.cn.push(boxLabelCfg);
20274 if(this.inputType != 'radio'){
20275 cfg.cn.push(hidden);
20283 * return the real input element.
20285 inputEl: function ()
20287 return this.el.select('input.roo-' + this.inputType,true).first();
20289 hiddenEl: function ()
20291 return this.el.select('input.roo-hidden-value',true).first();
20294 labelEl: function()
20296 return this.el.select('label.control-label',true).first();
20298 /* depricated... */
20302 return this.labelEl();
20305 boxLabelEl: function()
20307 return this.el.select('label.box-label',true).first();
20310 initEvents : function()
20312 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20314 this.inputEl().on('click', this.onClick, this);
20316 if (this.boxLabel) {
20317 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
20320 this.startValue = this.getValue();
20323 Roo.bootstrap.CheckBox.register(this);
20327 onClick : function()
20329 this.setChecked(!this.checked);
20332 setChecked : function(state,suppressEvent)
20334 this.startValue = this.getValue();
20336 if(this.inputType == 'radio'){
20338 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20339 e.dom.checked = false;
20342 this.inputEl().dom.checked = true;
20344 this.inputEl().dom.value = this.inputValue;
20346 if(suppressEvent !== true){
20347 this.fireEvent('check', this, true);
20355 this.checked = state;
20357 this.inputEl().dom.checked = state;
20360 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20362 if(suppressEvent !== true){
20363 this.fireEvent('check', this, state);
20369 getValue : function()
20371 if(this.inputType == 'radio'){
20372 return this.getGroupValue();
20375 return this.hiddenEl().dom.value;
20379 getGroupValue : function()
20381 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20385 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20388 setValue : function(v,suppressEvent)
20390 if(this.inputType == 'radio'){
20391 this.setGroupValue(v, suppressEvent);
20395 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20400 setGroupValue : function(v, suppressEvent)
20402 this.startValue = this.getValue();
20404 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20405 e.dom.checked = false;
20407 if(e.dom.value == v){
20408 e.dom.checked = true;
20412 if(suppressEvent !== true){
20413 this.fireEvent('check', this, true);
20421 validate : function()
20425 (this.inputType == 'radio' && this.validateRadio()) ||
20426 (this.inputType == 'checkbox' && this.validateCheckbox())
20432 this.markInvalid();
20436 validateRadio : function()
20438 if(this.allowBlank){
20444 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20445 if(!e.dom.checked){
20457 validateCheckbox : function()
20460 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20461 //return (this.getValue() == this.inputValue) ? true : false;
20464 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20472 for(var i in group){
20477 r = (group[i].getValue() == group[i].inputValue) ? true : false;
20484 * Mark this field as valid
20486 markValid : function()
20490 this.fireEvent('valid', this);
20492 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20495 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20502 if(this.inputType == 'radio'){
20503 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20504 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20505 e.findParent('.form-group', false, true).addClass(_this.validClass);
20512 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20513 this.el.findParent('.form-group', false, true).addClass(this.validClass);
20517 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20523 for(var i in group){
20524 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20525 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20530 * Mark this field as invalid
20531 * @param {String} msg The validation message
20533 markInvalid : function(msg)
20535 if(this.allowBlank){
20541 this.fireEvent('invalid', this, msg);
20543 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20546 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20550 label.markInvalid();
20553 if(this.inputType == 'radio'){
20554 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20555 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20556 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20563 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20564 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20568 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20574 for(var i in group){
20575 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20576 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20581 clearInvalid : function()
20583 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20585 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20587 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20590 label.iconEl.removeClass(label.validClass);
20591 label.iconEl.removeClass(label.invalidClass);
20595 disable : function()
20597 if(this.inputType != 'radio'){
20598 Roo.bootstrap.CheckBox.superclass.disable.call(this);
20605 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20606 _this.getActionEl().addClass(this.disabledClass);
20607 e.dom.disabled = true;
20611 this.disabled = true;
20612 this.fireEvent("disable", this);
20616 enable : function()
20618 if(this.inputType != 'radio'){
20619 Roo.bootstrap.CheckBox.superclass.enable.call(this);
20626 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20627 _this.getActionEl().removeClass(this.disabledClass);
20628 e.dom.disabled = false;
20632 this.disabled = false;
20633 this.fireEvent("enable", this);
20639 Roo.apply(Roo.bootstrap.CheckBox, {
20644 * register a CheckBox Group
20645 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20647 register : function(checkbox)
20649 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20650 this.groups[checkbox.groupId] = {};
20653 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20657 this.groups[checkbox.groupId][checkbox.name] = checkbox;
20661 * fetch a CheckBox Group based on the group ID
20662 * @param {string} the group ID
20663 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20665 get: function(groupId) {
20666 if (typeof(this.groups[groupId]) == 'undefined') {
20670 return this.groups[groupId] ;
20683 * @class Roo.bootstrap.Radio
20684 * @extends Roo.bootstrap.Component
20685 * Bootstrap Radio class
20686 * @cfg {String} boxLabel - the label associated
20687 * @cfg {String} value - the value of radio
20690 * Create a new Radio
20691 * @param {Object} config The config object
20693 Roo.bootstrap.Radio = function(config){
20694 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
20698 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
20704 getAutoCreate : function()
20708 cls : 'form-group radio',
20713 html : this.boxLabel
20721 initEvents : function()
20723 this.parent().register(this);
20725 this.el.on('click', this.onClick, this);
20729 onClick : function()
20731 this.setChecked(true);
20734 setChecked : function(state, suppressEvent)
20736 this.parent().setValue(this.value, suppressEvent);
20751 * @class Roo.bootstrap.SecurePass
20752 * @extends Roo.bootstrap.Input
20753 * Bootstrap SecurePass class
20757 * Create a new SecurePass
20758 * @param {Object} config The config object
20761 Roo.bootstrap.SecurePass = function (config) {
20762 // these go here, so the translation tool can replace them..
20764 PwdEmpty: "Please type a password, and then retype it to confirm.",
20765 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20766 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20767 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20768 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20769 FNInPwd: "Your password can't contain your first name. Please type a different password.",
20770 LNInPwd: "Your password can't contain your last name. Please type a different password.",
20771 TooWeak: "Your password is Too Weak."
20773 this.meterLabel = "Password strength:";
20774 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
20775 this.meterClass = [
20776 "roo-password-meter-tooweak",
20777 "roo-password-meter-weak",
20778 "roo-password-meter-medium",
20779 "roo-password-meter-strong",
20780 "roo-password-meter-grey"
20785 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
20788 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
20790 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
20792 * PwdEmpty: "Please type a password, and then retype it to confirm.",
20793 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20794 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20795 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20796 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20797 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
20798 * LNInPwd: "Your password can't contain your last name. Please type a different password."
20808 * @cfg {String/Object} Label for the strength meter (defaults to
20809 * 'Password strength:')
20814 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
20815 * ['Weak', 'Medium', 'Strong'])
20818 pwdStrengths: false,
20831 initEvents: function ()
20833 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
20835 if (this.el.is('input[type=password]') && Roo.isSafari) {
20836 this.el.on('keydown', this.SafariOnKeyDown, this);
20839 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
20842 onRender: function (ct, position)
20844 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
20845 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
20846 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
20848 this.trigger.createChild({
20853 cls: 'roo-password-meter-grey col-xs-12',
20856 //width: this.meterWidth + 'px'
20860 cls: 'roo-password-meter-text'
20866 if (this.hideTrigger) {
20867 this.trigger.setDisplayed(false);
20869 this.setSize(this.width || '', this.height || '');
20872 onDestroy: function ()
20874 if (this.trigger) {
20875 this.trigger.removeAllListeners();
20876 this.trigger.remove();
20879 this.wrap.remove();
20881 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
20884 checkStrength: function ()
20886 var pwd = this.inputEl().getValue();
20887 if (pwd == this._lastPwd) {
20892 if (this.ClientSideStrongPassword(pwd)) {
20894 } else if (this.ClientSideMediumPassword(pwd)) {
20896 } else if (this.ClientSideWeakPassword(pwd)) {
20902 Roo.log('strength1: ' + strength);
20904 //var pm = this.trigger.child('div/div/div').dom;
20905 var pm = this.trigger.child('div/div');
20906 pm.removeClass(this.meterClass);
20907 pm.addClass(this.meterClass[strength]);
20910 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
20912 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
20914 this._lastPwd = pwd;
20918 Roo.bootstrap.SecurePass.superclass.reset.call(this);
20920 this._lastPwd = '';
20922 var pm = this.trigger.child('div/div');
20923 pm.removeClass(this.meterClass);
20924 pm.addClass('roo-password-meter-grey');
20927 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
20930 this.inputEl().dom.type='password';
20933 validateValue: function (value)
20936 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
20939 if (value.length == 0) {
20940 if (this.allowBlank) {
20941 this.clearInvalid();
20945 this.markInvalid(this.errors.PwdEmpty);
20946 this.errorMsg = this.errors.PwdEmpty;
20954 if ('[\x21-\x7e]*'.match(value)) {
20955 this.markInvalid(this.errors.PwdBadChar);
20956 this.errorMsg = this.errors.PwdBadChar;
20959 if (value.length < 6) {
20960 this.markInvalid(this.errors.PwdShort);
20961 this.errorMsg = this.errors.PwdShort;
20964 if (value.length > 16) {
20965 this.markInvalid(this.errors.PwdLong);
20966 this.errorMsg = this.errors.PwdLong;
20970 if (this.ClientSideStrongPassword(value)) {
20972 } else if (this.ClientSideMediumPassword(value)) {
20974 } else if (this.ClientSideWeakPassword(value)) {
20981 if (strength < 2) {
20982 //this.markInvalid(this.errors.TooWeak);
20983 this.errorMsg = this.errors.TooWeak;
20988 console.log('strength2: ' + strength);
20990 //var pm = this.trigger.child('div/div/div').dom;
20992 var pm = this.trigger.child('div/div');
20993 pm.removeClass(this.meterClass);
20994 pm.addClass(this.meterClass[strength]);
20996 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
20998 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21000 this.errorMsg = '';
21004 CharacterSetChecks: function (type)
21007 this.fResult = false;
21010 isctype: function (character, type)
21013 case this.kCapitalLetter:
21014 if (character >= 'A' && character <= 'Z') {
21019 case this.kSmallLetter:
21020 if (character >= 'a' && character <= 'z') {
21026 if (character >= '0' && character <= '9') {
21031 case this.kPunctuation:
21032 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21043 IsLongEnough: function (pwd, size)
21045 return !(pwd == null || isNaN(size) || pwd.length < size);
21048 SpansEnoughCharacterSets: function (word, nb)
21050 if (!this.IsLongEnough(word, nb))
21055 var characterSetChecks = new Array(
21056 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21057 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21060 for (var index = 0; index < word.length; ++index) {
21061 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21062 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21063 characterSetChecks[nCharSet].fResult = true;
21070 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21071 if (characterSetChecks[nCharSet].fResult) {
21076 if (nCharSets < nb) {
21082 ClientSideStrongPassword: function (pwd)
21084 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21087 ClientSideMediumPassword: function (pwd)
21089 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21092 ClientSideWeakPassword: function (pwd)
21094 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21097 })//<script type="text/javascript">
21100 * Based Ext JS Library 1.1.1
21101 * Copyright(c) 2006-2007, Ext JS, LLC.
21107 * @class Roo.HtmlEditorCore
21108 * @extends Roo.Component
21109 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21111 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21114 Roo.HtmlEditorCore = function(config){
21117 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21122 * @event initialize
21123 * Fires when the editor is fully initialized (including the iframe)
21124 * @param {Roo.HtmlEditorCore} this
21129 * Fires when the editor is first receives the focus. Any insertion must wait
21130 * until after this event.
21131 * @param {Roo.HtmlEditorCore} this
21135 * @event beforesync
21136 * Fires before the textarea is updated with content from the editor iframe. Return false
21137 * to cancel the sync.
21138 * @param {Roo.HtmlEditorCore} this
21139 * @param {String} html
21143 * @event beforepush
21144 * Fires before the iframe editor is updated with content from the textarea. Return false
21145 * to cancel the push.
21146 * @param {Roo.HtmlEditorCore} this
21147 * @param {String} html
21152 * Fires when the textarea is updated with content from the editor iframe.
21153 * @param {Roo.HtmlEditorCore} this
21154 * @param {String} html
21159 * Fires when the iframe editor is updated with content from the textarea.
21160 * @param {Roo.HtmlEditorCore} this
21161 * @param {String} html
21166 * @event editorevent
21167 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21168 * @param {Roo.HtmlEditorCore} this
21174 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21176 // defaults : white / black...
21177 this.applyBlacklists();
21184 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
21188 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
21194 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21199 * @cfg {Number} height (in pixels)
21203 * @cfg {Number} width (in pixels)
21208 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21211 stylesheets: false,
21216 // private properties
21217 validationEvent : false,
21219 initialized : false,
21221 sourceEditMode : false,
21222 onFocus : Roo.emptyFn,
21224 hideMode:'offsets',
21228 // blacklist + whitelisted elements..
21235 * Protected method that will not generally be called directly. It
21236 * is called when the editor initializes the iframe with HTML contents. Override this method if you
21237 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21239 getDocMarkup : function(){
21243 // inherit styels from page...??
21244 if (this.stylesheets === false) {
21246 Roo.get(document.head).select('style').each(function(node) {
21247 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21250 Roo.get(document.head).select('link').each(function(node) {
21251 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21254 } else if (!this.stylesheets.length) {
21256 st = '<style type="text/css">' +
21257 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21263 st += '<style type="text/css">' +
21264 'IMG { cursor: pointer } ' +
21268 return '<html><head>' + st +
21269 //<style type="text/css">' +
21270 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21272 ' </head><body class="roo-htmleditor-body"></body></html>';
21276 onRender : function(ct, position)
21279 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21280 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21283 this.el.dom.style.border = '0 none';
21284 this.el.dom.setAttribute('tabIndex', -1);
21285 this.el.addClass('x-hidden hide');
21289 if(Roo.isIE){ // fix IE 1px bogus margin
21290 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21294 this.frameId = Roo.id();
21298 var iframe = this.owner.wrap.createChild({
21300 cls: 'form-control', // bootstrap..
21302 name: this.frameId,
21303 frameBorder : 'no',
21304 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
21309 this.iframe = iframe.dom;
21311 this.assignDocWin();
21313 this.doc.designMode = 'on';
21316 this.doc.write(this.getDocMarkup());
21320 var task = { // must defer to wait for browser to be ready
21322 //console.log("run task?" + this.doc.readyState);
21323 this.assignDocWin();
21324 if(this.doc.body || this.doc.readyState == 'complete'){
21326 this.doc.designMode="on";
21330 Roo.TaskMgr.stop(task);
21331 this.initEditor.defer(10, this);
21338 Roo.TaskMgr.start(task);
21343 onResize : function(w, h)
21345 Roo.log('resize: ' +w + ',' + h );
21346 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21350 if(typeof w == 'number'){
21352 this.iframe.style.width = w + 'px';
21354 if(typeof h == 'number'){
21356 this.iframe.style.height = h + 'px';
21358 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21365 * Toggles the editor between standard and source edit mode.
21366 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21368 toggleSourceEdit : function(sourceEditMode){
21370 this.sourceEditMode = sourceEditMode === true;
21372 if(this.sourceEditMode){
21374 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
21377 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21378 //this.iframe.className = '';
21381 //this.setSize(this.owner.wrap.getSize());
21382 //this.fireEvent('editmodechange', this, this.sourceEditMode);
21389 * Protected method that will not generally be called directly. If you need/want
21390 * custom HTML cleanup, this is the method you should override.
21391 * @param {String} html The HTML to be cleaned
21392 * return {String} The cleaned HTML
21394 cleanHtml : function(html){
21395 html = String(html);
21396 if(html.length > 5){
21397 if(Roo.isSafari){ // strip safari nonsense
21398 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21401 if(html == ' '){
21408 * HTML Editor -> Textarea
21409 * Protected method that will not generally be called directly. Syncs the contents
21410 * of the editor iframe with the textarea.
21412 syncValue : function(){
21413 if(this.initialized){
21414 var bd = (this.doc.body || this.doc.documentElement);
21415 //this.cleanUpPaste(); -- this is done else where and causes havoc..
21416 var html = bd.innerHTML;
21418 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21419 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21421 html = '<div style="'+m[0]+'">' + html + '</div>';
21424 html = this.cleanHtml(html);
21425 // fix up the special chars.. normaly like back quotes in word...
21426 // however we do not want to do this with chinese..
21427 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21428 var cc = b.charCodeAt();
21430 (cc >= 0x4E00 && cc < 0xA000 ) ||
21431 (cc >= 0x3400 && cc < 0x4E00 ) ||
21432 (cc >= 0xf900 && cc < 0xfb00 )
21438 if(this.owner.fireEvent('beforesync', this, html) !== false){
21439 this.el.dom.value = html;
21440 this.owner.fireEvent('sync', this, html);
21446 * Protected method that will not generally be called directly. Pushes the value of the textarea
21447 * into the iframe editor.
21449 pushValue : function(){
21450 if(this.initialized){
21451 var v = this.el.dom.value.trim();
21453 // if(v.length < 1){
21457 if(this.owner.fireEvent('beforepush', this, v) !== false){
21458 var d = (this.doc.body || this.doc.documentElement);
21460 this.cleanUpPaste();
21461 this.el.dom.value = d.innerHTML;
21462 this.owner.fireEvent('push', this, v);
21468 deferFocus : function(){
21469 this.focus.defer(10, this);
21473 focus : function(){
21474 if(this.win && !this.sourceEditMode){
21481 assignDocWin: function()
21483 var iframe = this.iframe;
21486 this.doc = iframe.contentWindow.document;
21487 this.win = iframe.contentWindow;
21489 // if (!Roo.get(this.frameId)) {
21492 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21493 // this.win = Roo.get(this.frameId).dom.contentWindow;
21495 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21499 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21500 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21505 initEditor : function(){
21506 //console.log("INIT EDITOR");
21507 this.assignDocWin();
21511 this.doc.designMode="on";
21513 this.doc.write(this.getDocMarkup());
21516 var dbody = (this.doc.body || this.doc.documentElement);
21517 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21518 // this copies styles from the containing element into thsi one..
21519 // not sure why we need all of this..
21520 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21522 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21523 //ss['background-attachment'] = 'fixed'; // w3c
21524 dbody.bgProperties = 'fixed'; // ie
21525 //Roo.DomHelper.applyStyles(dbody, ss);
21526 Roo.EventManager.on(this.doc, {
21527 //'mousedown': this.onEditorEvent,
21528 'mouseup': this.onEditorEvent,
21529 'dblclick': this.onEditorEvent,
21530 'click': this.onEditorEvent,
21531 'keyup': this.onEditorEvent,
21536 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21538 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21539 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21541 this.initialized = true;
21543 this.owner.fireEvent('initialize', this);
21548 onDestroy : function(){
21554 //for (var i =0; i < this.toolbars.length;i++) {
21555 // // fixme - ask toolbars for heights?
21556 // this.toolbars[i].onDestroy();
21559 //this.wrap.dom.innerHTML = '';
21560 //this.wrap.remove();
21565 onFirstFocus : function(){
21567 this.assignDocWin();
21570 this.activated = true;
21573 if(Roo.isGecko){ // prevent silly gecko errors
21575 var s = this.win.getSelection();
21576 if(!s.focusNode || s.focusNode.nodeType != 3){
21577 var r = s.getRangeAt(0);
21578 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21583 this.execCmd('useCSS', true);
21584 this.execCmd('styleWithCSS', false);
21587 this.owner.fireEvent('activate', this);
21591 adjustFont: function(btn){
21592 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21593 //if(Roo.isSafari){ // safari
21596 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21597 if(Roo.isSafari){ // safari
21598 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21599 v = (v < 10) ? 10 : v;
21600 v = (v > 48) ? 48 : v;
21601 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21606 v = Math.max(1, v+adjust);
21608 this.execCmd('FontSize', v );
21611 onEditorEvent : function(e)
21613 this.owner.fireEvent('editorevent', this, e);
21614 // this.updateToolbar();
21615 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21618 insertTag : function(tg)
21620 // could be a bit smarter... -> wrap the current selected tRoo..
21621 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
21623 range = this.createRange(this.getSelection());
21624 var wrappingNode = this.doc.createElement(tg.toLowerCase());
21625 wrappingNode.appendChild(range.extractContents());
21626 range.insertNode(wrappingNode);
21633 this.execCmd("formatblock", tg);
21637 insertText : function(txt)
21641 var range = this.createRange();
21642 range.deleteContents();
21643 //alert(Sender.getAttribute('label'));
21645 range.insertNode(this.doc.createTextNode(txt));
21651 * Executes a Midas editor command on the editor document and performs necessary focus and
21652 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21653 * @param {String} cmd The Midas command
21654 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21656 relayCmd : function(cmd, value){
21658 this.execCmd(cmd, value);
21659 this.owner.fireEvent('editorevent', this);
21660 //this.updateToolbar();
21661 this.owner.deferFocus();
21665 * Executes a Midas editor command directly on the editor document.
21666 * For visual commands, you should use {@link #relayCmd} instead.
21667 * <b>This should only be called after the editor is initialized.</b>
21668 * @param {String} cmd The Midas command
21669 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21671 execCmd : function(cmd, value){
21672 this.doc.execCommand(cmd, false, value === undefined ? null : value);
21679 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21681 * @param {String} text | dom node..
21683 insertAtCursor : function(text)
21686 if(!this.activated){
21692 var r = this.doc.selection.createRange();
21703 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21707 // from jquery ui (MIT licenced)
21709 var win = this.win;
21711 if (win.getSelection && win.getSelection().getRangeAt) {
21712 range = win.getSelection().getRangeAt(0);
21713 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21714 range.insertNode(node);
21715 } else if (win.document.selection && win.document.selection.createRange) {
21716 // no firefox support
21717 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21718 win.document.selection.createRange().pasteHTML(txt);
21720 // no firefox support
21721 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21722 this.execCmd('InsertHTML', txt);
21731 mozKeyPress : function(e){
21733 var c = e.getCharCode(), cmd;
21736 c = String.fromCharCode(c).toLowerCase();
21750 this.cleanUpPaste.defer(100, this);
21758 e.preventDefault();
21766 fixKeys : function(){ // load time branching for fastest keydown performance
21768 return function(e){
21769 var k = e.getKey(), r;
21772 r = this.doc.selection.createRange();
21775 r.pasteHTML('    ');
21782 r = this.doc.selection.createRange();
21784 var target = r.parentElement();
21785 if(!target || target.tagName.toLowerCase() != 'li'){
21787 r.pasteHTML('<br />');
21793 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21794 this.cleanUpPaste.defer(100, this);
21800 }else if(Roo.isOpera){
21801 return function(e){
21802 var k = e.getKey();
21806 this.execCmd('InsertHTML','    ');
21809 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21810 this.cleanUpPaste.defer(100, this);
21815 }else if(Roo.isSafari){
21816 return function(e){
21817 var k = e.getKey();
21821 this.execCmd('InsertText','\t');
21825 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21826 this.cleanUpPaste.defer(100, this);
21834 getAllAncestors: function()
21836 var p = this.getSelectedNode();
21839 a.push(p); // push blank onto stack..
21840 p = this.getParentElement();
21844 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21848 a.push(this.doc.body);
21852 lastSelNode : false,
21855 getSelection : function()
21857 this.assignDocWin();
21858 return Roo.isIE ? this.doc.selection : this.win.getSelection();
21861 getSelectedNode: function()
21863 // this may only work on Gecko!!!
21865 // should we cache this!!!!
21870 var range = this.createRange(this.getSelection()).cloneRange();
21873 var parent = range.parentElement();
21875 var testRange = range.duplicate();
21876 testRange.moveToElementText(parent);
21877 if (testRange.inRange(range)) {
21880 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21883 parent = parent.parentElement;
21888 // is ancestor a text element.
21889 var ac = range.commonAncestorContainer;
21890 if (ac.nodeType == 3) {
21891 ac = ac.parentNode;
21894 var ar = ac.childNodes;
21897 var other_nodes = [];
21898 var has_other_nodes = false;
21899 for (var i=0;i<ar.length;i++) {
21900 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
21903 // fullly contained node.
21905 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21910 // probably selected..
21911 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21912 other_nodes.push(ar[i]);
21916 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
21921 has_other_nodes = true;
21923 if (!nodes.length && other_nodes.length) {
21924 nodes= other_nodes;
21926 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21932 createRange: function(sel)
21934 // this has strange effects when using with
21935 // top toolbar - not sure if it's a great idea.
21936 //this.editor.contentWindow.focus();
21937 if (typeof sel != "undefined") {
21939 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21941 return this.doc.createRange();
21944 return this.doc.createRange();
21947 getParentElement: function()
21950 this.assignDocWin();
21951 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21953 var range = this.createRange(sel);
21956 var p = range.commonAncestorContainer;
21957 while (p.nodeType == 3) { // text node
21968 * Range intersection.. the hard stuff...
21972 * [ -- selected range --- ]
21976 * if end is before start or hits it. fail.
21977 * if start is after end or hits it fail.
21979 * if either hits (but other is outside. - then it's not
21985 // @see http://www.thismuchiknow.co.uk/?p=64.
21986 rangeIntersectsNode : function(range, node)
21988 var nodeRange = node.ownerDocument.createRange();
21990 nodeRange.selectNode(node);
21992 nodeRange.selectNodeContents(node);
21995 var rangeStartRange = range.cloneRange();
21996 rangeStartRange.collapse(true);
21998 var rangeEndRange = range.cloneRange();
21999 rangeEndRange.collapse(false);
22001 var nodeStartRange = nodeRange.cloneRange();
22002 nodeStartRange.collapse(true);
22004 var nodeEndRange = nodeRange.cloneRange();
22005 nodeEndRange.collapse(false);
22007 return rangeStartRange.compareBoundaryPoints(
22008 Range.START_TO_START, nodeEndRange) == -1 &&
22009 rangeEndRange.compareBoundaryPoints(
22010 Range.START_TO_START, nodeStartRange) == 1;
22014 rangeCompareNode : function(range, node)
22016 var nodeRange = node.ownerDocument.createRange();
22018 nodeRange.selectNode(node);
22020 nodeRange.selectNodeContents(node);
22024 range.collapse(true);
22026 nodeRange.collapse(true);
22028 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22029 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
22031 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22033 var nodeIsBefore = ss == 1;
22034 var nodeIsAfter = ee == -1;
22036 if (nodeIsBefore && nodeIsAfter) {
22039 if (!nodeIsBefore && nodeIsAfter) {
22040 return 1; //right trailed.
22043 if (nodeIsBefore && !nodeIsAfter) {
22044 return 2; // left trailed.
22050 // private? - in a new class?
22051 cleanUpPaste : function()
22053 // cleans up the whole document..
22054 Roo.log('cleanuppaste');
22056 this.cleanUpChildren(this.doc.body);
22057 var clean = this.cleanWordChars(this.doc.body.innerHTML);
22058 if (clean != this.doc.body.innerHTML) {
22059 this.doc.body.innerHTML = clean;
22064 cleanWordChars : function(input) {// change the chars to hex code
22065 var he = Roo.HtmlEditorCore;
22067 var output = input;
22068 Roo.each(he.swapCodes, function(sw) {
22069 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22071 output = output.replace(swapper, sw[1]);
22078 cleanUpChildren : function (n)
22080 if (!n.childNodes.length) {
22083 for (var i = n.childNodes.length-1; i > -1 ; i--) {
22084 this.cleanUpChild(n.childNodes[i]);
22091 cleanUpChild : function (node)
22094 //console.log(node);
22095 if (node.nodeName == "#text") {
22096 // clean up silly Windows -- stuff?
22099 if (node.nodeName == "#comment") {
22100 node.parentNode.removeChild(node);
22101 // clean up silly Windows -- stuff?
22104 var lcname = node.tagName.toLowerCase();
22105 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22106 // whitelist of tags..
22108 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22110 node.parentNode.removeChild(node);
22115 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22117 // remove <a name=....> as rendering on yahoo mailer is borked with this.
22118 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22120 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22121 // remove_keep_children = true;
22124 if (remove_keep_children) {
22125 this.cleanUpChildren(node);
22126 // inserts everything just before this node...
22127 while (node.childNodes.length) {
22128 var cn = node.childNodes[0];
22129 node.removeChild(cn);
22130 node.parentNode.insertBefore(cn, node);
22132 node.parentNode.removeChild(node);
22136 if (!node.attributes || !node.attributes.length) {
22137 this.cleanUpChildren(node);
22141 function cleanAttr(n,v)
22144 if (v.match(/^\./) || v.match(/^\//)) {
22147 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
22150 if (v.match(/^#/)) {
22153 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22154 node.removeAttribute(n);
22158 var cwhite = this.cwhite;
22159 var cblack = this.cblack;
22161 function cleanStyle(n,v)
22163 if (v.match(/expression/)) { //XSS?? should we even bother..
22164 node.removeAttribute(n);
22168 var parts = v.split(/;/);
22171 Roo.each(parts, function(p) {
22172 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22176 var l = p.split(':').shift().replace(/\s+/g,'');
22177 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22179 if ( cwhite.length && cblack.indexOf(l) > -1) {
22180 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22181 //node.removeAttribute(n);
22185 // only allow 'c whitelisted system attributes'
22186 if ( cwhite.length && cwhite.indexOf(l) < 0) {
22187 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22188 //node.removeAttribute(n);
22198 if (clean.length) {
22199 node.setAttribute(n, clean.join(';'));
22201 node.removeAttribute(n);
22207 for (var i = node.attributes.length-1; i > -1 ; i--) {
22208 var a = node.attributes[i];
22211 if (a.name.toLowerCase().substr(0,2)=='on') {
22212 node.removeAttribute(a.name);
22215 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22216 node.removeAttribute(a.name);
22219 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22220 cleanAttr(a.name,a.value); // fixme..
22223 if (a.name == 'style') {
22224 cleanStyle(a.name,a.value);
22227 /// clean up MS crap..
22228 // tecnically this should be a list of valid class'es..
22231 if (a.name == 'class') {
22232 if (a.value.match(/^Mso/)) {
22233 node.className = '';
22236 if (a.value.match(/^body$/)) {
22237 node.className = '';
22248 this.cleanUpChildren(node);
22254 * Clean up MS wordisms...
22256 cleanWord : function(node)
22261 this.cleanWord(this.doc.body);
22264 if (node.nodeName == "#text") {
22265 // clean up silly Windows -- stuff?
22268 if (node.nodeName == "#comment") {
22269 node.parentNode.removeChild(node);
22270 // clean up silly Windows -- stuff?
22274 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22275 node.parentNode.removeChild(node);
22279 // remove - but keep children..
22280 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22281 while (node.childNodes.length) {
22282 var cn = node.childNodes[0];
22283 node.removeChild(cn);
22284 node.parentNode.insertBefore(cn, node);
22286 node.parentNode.removeChild(node);
22287 this.iterateChildren(node, this.cleanWord);
22291 if (node.className.length) {
22293 var cn = node.className.split(/\W+/);
22295 Roo.each(cn, function(cls) {
22296 if (cls.match(/Mso[a-zA-Z]+/)) {
22301 node.className = cna.length ? cna.join(' ') : '';
22303 node.removeAttribute("class");
22307 if (node.hasAttribute("lang")) {
22308 node.removeAttribute("lang");
22311 if (node.hasAttribute("style")) {
22313 var styles = node.getAttribute("style").split(";");
22315 Roo.each(styles, function(s) {
22316 if (!s.match(/:/)) {
22319 var kv = s.split(":");
22320 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22323 // what ever is left... we allow.
22326 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22327 if (!nstyle.length) {
22328 node.removeAttribute('style');
22331 this.iterateChildren(node, this.cleanWord);
22337 * iterateChildren of a Node, calling fn each time, using this as the scole..
22338 * @param {DomNode} node node to iterate children of.
22339 * @param {Function} fn method of this class to call on each item.
22341 iterateChildren : function(node, fn)
22343 if (!node.childNodes.length) {
22346 for (var i = node.childNodes.length-1; i > -1 ; i--) {
22347 fn.call(this, node.childNodes[i])
22353 * cleanTableWidths.
22355 * Quite often pasting from word etc.. results in tables with column and widths.
22356 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22359 cleanTableWidths : function(node)
22364 this.cleanTableWidths(this.doc.body);
22369 if (node.nodeName == "#text" || node.nodeName == "#comment") {
22372 Roo.log(node.tagName);
22373 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22374 this.iterateChildren(node, this.cleanTableWidths);
22377 if (node.hasAttribute('width')) {
22378 node.removeAttribute('width');
22382 if (node.hasAttribute("style")) {
22385 var styles = node.getAttribute("style").split(";");
22387 Roo.each(styles, function(s) {
22388 if (!s.match(/:/)) {
22391 var kv = s.split(":");
22392 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22395 // what ever is left... we allow.
22398 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22399 if (!nstyle.length) {
22400 node.removeAttribute('style');
22404 this.iterateChildren(node, this.cleanTableWidths);
22412 domToHTML : function(currentElement, depth, nopadtext) {
22414 depth = depth || 0;
22415 nopadtext = nopadtext || false;
22417 if (!currentElement) {
22418 return this.domToHTML(this.doc.body);
22421 //Roo.log(currentElement);
22423 var allText = false;
22424 var nodeName = currentElement.nodeName;
22425 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22427 if (nodeName == '#text') {
22429 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22434 if (nodeName != 'BODY') {
22437 // Prints the node tagName, such as <A>, <IMG>, etc
22440 for(i = 0; i < currentElement.attributes.length;i++) {
22442 var aname = currentElement.attributes.item(i).name;
22443 if (!currentElement.attributes.item(i).value.length) {
22446 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22449 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22458 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22461 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22466 // Traverse the tree
22468 var currentElementChild = currentElement.childNodes.item(i);
22469 var allText = true;
22470 var innerHTML = '';
22472 while (currentElementChild) {
22473 // Formatting code (indent the tree so it looks nice on the screen)
22474 var nopad = nopadtext;
22475 if (lastnode == 'SPAN') {
22479 if (currentElementChild.nodeName == '#text') {
22480 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22481 toadd = nopadtext ? toadd : toadd.trim();
22482 if (!nopad && toadd.length > 80) {
22483 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
22485 innerHTML += toadd;
22488 currentElementChild = currentElement.childNodes.item(i);
22494 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
22496 // Recursively traverse the tree structure of the child node
22497 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
22498 lastnode = currentElementChild.nodeName;
22500 currentElementChild=currentElement.childNodes.item(i);
22506 // The remaining code is mostly for formatting the tree
22507 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
22512 ret+= "</"+tagName+">";
22518 applyBlacklists : function()
22520 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
22521 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
22525 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22526 if (b.indexOf(tag) > -1) {
22529 this.white.push(tag);
22533 Roo.each(w, function(tag) {
22534 if (b.indexOf(tag) > -1) {
22537 if (this.white.indexOf(tag) > -1) {
22540 this.white.push(tag);
22545 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22546 if (w.indexOf(tag) > -1) {
22549 this.black.push(tag);
22553 Roo.each(b, function(tag) {
22554 if (w.indexOf(tag) > -1) {
22557 if (this.black.indexOf(tag) > -1) {
22560 this.black.push(tag);
22565 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
22566 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
22570 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22571 if (b.indexOf(tag) > -1) {
22574 this.cwhite.push(tag);
22578 Roo.each(w, function(tag) {
22579 if (b.indexOf(tag) > -1) {
22582 if (this.cwhite.indexOf(tag) > -1) {
22585 this.cwhite.push(tag);
22590 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22591 if (w.indexOf(tag) > -1) {
22594 this.cblack.push(tag);
22598 Roo.each(b, function(tag) {
22599 if (w.indexOf(tag) > -1) {
22602 if (this.cblack.indexOf(tag) > -1) {
22605 this.cblack.push(tag);
22610 setStylesheets : function(stylesheets)
22612 if(typeof(stylesheets) == 'string'){
22613 Roo.get(this.iframe.contentDocument.head).createChild({
22615 rel : 'stylesheet',
22624 Roo.each(stylesheets, function(s) {
22629 Roo.get(_this.iframe.contentDocument.head).createChild({
22631 rel : 'stylesheet',
22640 removeStylesheets : function()
22644 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22649 // hide stuff that is not compatible
22663 * @event specialkey
22667 * @cfg {String} fieldClass @hide
22670 * @cfg {String} focusClass @hide
22673 * @cfg {String} autoCreate @hide
22676 * @cfg {String} inputType @hide
22679 * @cfg {String} invalidClass @hide
22682 * @cfg {String} invalidText @hide
22685 * @cfg {String} msgFx @hide
22688 * @cfg {String} validateOnBlur @hide
22692 Roo.HtmlEditorCore.white = [
22693 'area', 'br', 'img', 'input', 'hr', 'wbr',
22695 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
22696 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
22697 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
22698 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
22699 'table', 'ul', 'xmp',
22701 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
22704 'dir', 'menu', 'ol', 'ul', 'dl',
22710 Roo.HtmlEditorCore.black = [
22711 // 'embed', 'object', // enable - backend responsiblity to clean thiese
22713 'base', 'basefont', 'bgsound', 'blink', 'body',
22714 'frame', 'frameset', 'head', 'html', 'ilayer',
22715 'iframe', 'layer', 'link', 'meta', 'object',
22716 'script', 'style' ,'title', 'xml' // clean later..
22718 Roo.HtmlEditorCore.clean = [
22719 'script', 'style', 'title', 'xml'
22721 Roo.HtmlEditorCore.remove = [
22726 Roo.HtmlEditorCore.ablack = [
22730 Roo.HtmlEditorCore.aclean = [
22731 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
22735 Roo.HtmlEditorCore.pwhite= [
22736 'http', 'https', 'mailto'
22739 // white listed style attributes.
22740 Roo.HtmlEditorCore.cwhite= [
22741 // 'text-align', /// default is to allow most things..
22747 // black listed style attributes.
22748 Roo.HtmlEditorCore.cblack= [
22749 // 'font-size' -- this can be set by the project
22753 Roo.HtmlEditorCore.swapCodes =[
22772 * @class Roo.bootstrap.HtmlEditor
22773 * @extends Roo.bootstrap.TextArea
22774 * Bootstrap HtmlEditor class
22777 * Create a new HtmlEditor
22778 * @param {Object} config The config object
22781 Roo.bootstrap.HtmlEditor = function(config){
22782 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
22783 if (!this.toolbars) {
22784 this.toolbars = [];
22787 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22790 * @event initialize
22791 * Fires when the editor is fully initialized (including the iframe)
22792 * @param {HtmlEditor} this
22797 * Fires when the editor is first receives the focus. Any insertion must wait
22798 * until after this event.
22799 * @param {HtmlEditor} this
22803 * @event beforesync
22804 * Fires before the textarea is updated with content from the editor iframe. Return false
22805 * to cancel the sync.
22806 * @param {HtmlEditor} this
22807 * @param {String} html
22811 * @event beforepush
22812 * Fires before the iframe editor is updated with content from the textarea. Return false
22813 * to cancel the push.
22814 * @param {HtmlEditor} this
22815 * @param {String} html
22820 * Fires when the textarea is updated with content from the editor iframe.
22821 * @param {HtmlEditor} this
22822 * @param {String} html
22827 * Fires when the iframe editor is updated with content from the textarea.
22828 * @param {HtmlEditor} this
22829 * @param {String} html
22833 * @event editmodechange
22834 * Fires when the editor switches edit modes
22835 * @param {HtmlEditor} this
22836 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22838 editmodechange: true,
22840 * @event editorevent
22841 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22842 * @param {HtmlEditor} this
22846 * @event firstfocus
22847 * Fires when on first focus - needed by toolbars..
22848 * @param {HtmlEditor} this
22853 * Auto save the htmlEditor value as a file into Events
22854 * @param {HtmlEditor} this
22858 * @event savedpreview
22859 * preview the saved version of htmlEditor
22860 * @param {HtmlEditor} this
22867 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
22871 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22876 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
22881 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
22886 * @cfg {Number} height (in pixels)
22890 * @cfg {Number} width (in pixels)
22895 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22898 stylesheets: false,
22903 // private properties
22904 validationEvent : false,
22906 initialized : false,
22909 onFocus : Roo.emptyFn,
22911 hideMode:'offsets',
22913 tbContainer : false,
22915 toolbarContainer :function() {
22916 return this.wrap.select('.x-html-editor-tb',true).first();
22920 * Protected method that will not generally be called directly. It
22921 * is called when the editor creates its toolbar. Override this method if you need to
22922 * add custom toolbar buttons.
22923 * @param {HtmlEditor} editor
22925 createToolbar : function(){
22926 Roo.log('renewing');
22927 Roo.log("create toolbars");
22929 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
22930 this.toolbars[0].render(this.toolbarContainer());
22934 // if (!editor.toolbars || !editor.toolbars.length) {
22935 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
22938 // for (var i =0 ; i < editor.toolbars.length;i++) {
22939 // editor.toolbars[i] = Roo.factory(
22940 // typeof(editor.toolbars[i]) == 'string' ?
22941 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
22942 // Roo.bootstrap.HtmlEditor);
22943 // editor.toolbars[i].init(editor);
22949 onRender : function(ct, position)
22951 // Roo.log("Call onRender: " + this.xtype);
22953 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
22955 this.wrap = this.inputEl().wrap({
22956 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22959 this.editorcore.onRender(ct, position);
22961 if (this.resizable) {
22962 this.resizeEl = new Roo.Resizable(this.wrap, {
22966 minHeight : this.height,
22967 height: this.height,
22968 handles : this.resizable,
22971 resize : function(r, w, h) {
22972 _t.onResize(w,h); // -something
22978 this.createToolbar(this);
22981 if(!this.width && this.resizable){
22982 this.setSize(this.wrap.getSize());
22984 if (this.resizeEl) {
22985 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22986 // should trigger onReize..
22992 onResize : function(w, h)
22994 Roo.log('resize: ' +w + ',' + h );
22995 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
22999 if(this.inputEl() ){
23000 if(typeof w == 'number'){
23001 var aw = w - this.wrap.getFrameWidth('lr');
23002 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23005 if(typeof h == 'number'){
23006 var tbh = -11; // fixme it needs to tool bar size!
23007 for (var i =0; i < this.toolbars.length;i++) {
23008 // fixme - ask toolbars for heights?
23009 tbh += this.toolbars[i].el.getHeight();
23010 //if (this.toolbars[i].footer) {
23011 // tbh += this.toolbars[i].footer.el.getHeight();
23019 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23020 ah -= 5; // knock a few pixes off for look..
23021 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23025 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23026 this.editorcore.onResize(ew,eh);
23031 * Toggles the editor between standard and source edit mode.
23032 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23034 toggleSourceEdit : function(sourceEditMode)
23036 this.editorcore.toggleSourceEdit(sourceEditMode);
23038 if(this.editorcore.sourceEditMode){
23039 Roo.log('editor - showing textarea');
23042 // Roo.log(this.syncValue());
23044 this.inputEl().removeClass(['hide', 'x-hidden']);
23045 this.inputEl().dom.removeAttribute('tabIndex');
23046 this.inputEl().focus();
23048 Roo.log('editor - hiding textarea');
23050 // Roo.log(this.pushValue());
23053 this.inputEl().addClass(['hide', 'x-hidden']);
23054 this.inputEl().dom.setAttribute('tabIndex', -1);
23055 //this.deferFocus();
23058 if(this.resizable){
23059 this.setSize(this.wrap.getSize());
23062 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23065 // private (for BoxComponent)
23066 adjustSize : Roo.BoxComponent.prototype.adjustSize,
23068 // private (for BoxComponent)
23069 getResizeEl : function(){
23073 // private (for BoxComponent)
23074 getPositionEl : function(){
23079 initEvents : function(){
23080 this.originalValue = this.getValue();
23084 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23087 // markInvalid : Roo.emptyFn,
23089 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23092 // clearInvalid : Roo.emptyFn,
23094 setValue : function(v){
23095 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23096 this.editorcore.pushValue();
23101 deferFocus : function(){
23102 this.focus.defer(10, this);
23106 focus : function(){
23107 this.editorcore.focus();
23113 onDestroy : function(){
23119 for (var i =0; i < this.toolbars.length;i++) {
23120 // fixme - ask toolbars for heights?
23121 this.toolbars[i].onDestroy();
23124 this.wrap.dom.innerHTML = '';
23125 this.wrap.remove();
23130 onFirstFocus : function(){
23131 //Roo.log("onFirstFocus");
23132 this.editorcore.onFirstFocus();
23133 for (var i =0; i < this.toolbars.length;i++) {
23134 this.toolbars[i].onFirstFocus();
23140 syncValue : function()
23142 this.editorcore.syncValue();
23145 pushValue : function()
23147 this.editorcore.pushValue();
23151 // hide stuff that is not compatible
23165 * @event specialkey
23169 * @cfg {String} fieldClass @hide
23172 * @cfg {String} focusClass @hide
23175 * @cfg {String} autoCreate @hide
23178 * @cfg {String} inputType @hide
23181 * @cfg {String} invalidClass @hide
23184 * @cfg {String} invalidText @hide
23187 * @cfg {String} msgFx @hide
23190 * @cfg {String} validateOnBlur @hide
23199 Roo.namespace('Roo.bootstrap.htmleditor');
23201 * @class Roo.bootstrap.HtmlEditorToolbar1
23206 new Roo.bootstrap.HtmlEditor({
23209 new Roo.bootstrap.HtmlEditorToolbar1({
23210 disable : { fonts: 1 , format: 1, ..., ... , ...],
23216 * @cfg {Object} disable List of elements to disable..
23217 * @cfg {Array} btns List of additional buttons.
23221 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23224 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23227 Roo.apply(this, config);
23229 // default disabled, based on 'good practice'..
23230 this.disable = this.disable || {};
23231 Roo.applyIf(this.disable, {
23234 specialElements : true
23236 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23238 this.editor = config.editor;
23239 this.editorcore = config.editor.editorcore;
23241 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23243 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23244 // dont call parent... till later.
23246 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
23251 editorcore : false,
23256 "h1","h2","h3","h4","h5","h6",
23258 "abbr", "acronym", "address", "cite", "samp", "var",
23262 onRender : function(ct, position)
23264 // Roo.log("Call onRender: " + this.xtype);
23266 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23268 this.el.dom.style.marginBottom = '0';
23270 var editorcore = this.editorcore;
23271 var editor= this.editor;
23274 var btn = function(id,cmd , toggle, handler, html){
23276 var event = toggle ? 'toggle' : 'click';
23281 xns: Roo.bootstrap,
23284 enableToggle:toggle !== false,
23286 pressed : toggle ? false : null,
23289 a.listeners[toggle ? 'toggle' : 'click'] = function() {
23290 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
23296 // var cb_box = function...
23301 xns: Roo.bootstrap,
23302 glyphicon : 'font',
23306 xns: Roo.bootstrap,
23310 Roo.each(this.formats, function(f) {
23311 style.menu.items.push({
23313 xns: Roo.bootstrap,
23314 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23319 editorcore.insertTag(this.tagname);
23326 children.push(style);
23328 btn('bold',false,true);
23329 btn('italic',false,true);
23330 btn('align-left', 'justifyleft',true);
23331 btn('align-center', 'justifycenter',true);
23332 btn('align-right' , 'justifyright',true);
23333 btn('link', false, false, function(btn) {
23334 //Roo.log("create link?");
23335 var url = prompt(this.createLinkText, this.defaultLinkValue);
23336 if(url && url != 'http:/'+'/'){
23337 this.editorcore.relayCmd('createlink', url);
23340 btn('list','insertunorderedlist',true);
23341 btn('pencil', false,true, function(btn){
23343 this.toggleSourceEdit(btn.pressed);
23346 if (this.editor.btns.length > 0) {
23347 for (var i = 0; i<this.editor.btns.length; i++) {
23348 children.push(this.editor.btns[i]);
23356 xns: Roo.bootstrap,
23361 xns: Roo.bootstrap,
23366 cog.menu.items.push({
23368 xns: Roo.bootstrap,
23369 html : Clean styles,
23374 editorcore.insertTag(this.tagname);
23383 this.xtype = 'NavSimplebar';
23385 for(var i=0;i< children.length;i++) {
23387 this.buttons.add(this.addxtypeChild(children[i]));
23391 editor.on('editorevent', this.updateToolbar, this);
23393 onBtnClick : function(id)
23395 this.editorcore.relayCmd(id);
23396 this.editorcore.focus();
23400 * Protected method that will not generally be called directly. It triggers
23401 * a toolbar update by reading the markup state of the current selection in the editor.
23403 updateToolbar: function(){
23405 if(!this.editorcore.activated){
23406 this.editor.onFirstFocus(); // is this neeed?
23410 var btns = this.buttons;
23411 var doc = this.editorcore.doc;
23412 btns.get('bold').setActive(doc.queryCommandState('bold'));
23413 btns.get('italic').setActive(doc.queryCommandState('italic'));
23414 //btns.get('underline').setActive(doc.queryCommandState('underline'));
23416 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23417 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23418 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23420 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23421 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23424 var ans = this.editorcore.getAllAncestors();
23425 if (this.formatCombo) {
23428 var store = this.formatCombo.store;
23429 this.formatCombo.setValue("");
23430 for (var i =0; i < ans.length;i++) {
23431 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23433 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23441 // hides menus... - so this cant be on a menu...
23442 Roo.bootstrap.MenuMgr.hideAll();
23444 Roo.bootstrap.MenuMgr.hideAll();
23445 //this.editorsyncValue();
23447 onFirstFocus: function() {
23448 this.buttons.each(function(item){
23452 toggleSourceEdit : function(sourceEditMode){
23455 if(sourceEditMode){
23456 Roo.log("disabling buttons");
23457 this.buttons.each( function(item){
23458 if(item.cmd != 'pencil'){
23464 Roo.log("enabling buttons");
23465 if(this.editorcore.initialized){
23466 this.buttons.each( function(item){
23472 Roo.log("calling toggole on editor");
23473 // tell the editor that it's been pressed..
23474 this.editor.toggleSourceEdit(sourceEditMode);
23484 * @class Roo.bootstrap.Table.AbstractSelectionModel
23485 * @extends Roo.util.Observable
23486 * Abstract base class for grid SelectionModels. It provides the interface that should be
23487 * implemented by descendant classes. This class should not be directly instantiated.
23490 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23491 this.locked = false;
23492 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23496 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
23497 /** @ignore Called by the grid automatically. Do not call directly. */
23498 init : function(grid){
23504 * Locks the selections.
23507 this.locked = true;
23511 * Unlocks the selections.
23513 unlock : function(){
23514 this.locked = false;
23518 * Returns true if the selections are locked.
23519 * @return {Boolean}
23521 isLocked : function(){
23522 return this.locked;
23526 * @extends Roo.bootstrap.Table.AbstractSelectionModel
23527 * @class Roo.bootstrap.Table.RowSelectionModel
23528 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23529 * It supports multiple selections and keyboard selection/navigation.
23531 * @param {Object} config
23534 Roo.bootstrap.Table.RowSelectionModel = function(config){
23535 Roo.apply(this, config);
23536 this.selections = new Roo.util.MixedCollection(false, function(o){
23541 this.lastActive = false;
23545 * @event selectionchange
23546 * Fires when the selection changes
23547 * @param {SelectionModel} this
23549 "selectionchange" : true,
23551 * @event afterselectionchange
23552 * Fires after the selection changes (eg. by key press or clicking)
23553 * @param {SelectionModel} this
23555 "afterselectionchange" : true,
23557 * @event beforerowselect
23558 * Fires when a row is selected being selected, return false to cancel.
23559 * @param {SelectionModel} this
23560 * @param {Number} rowIndex The selected index
23561 * @param {Boolean} keepExisting False if other selections will be cleared
23563 "beforerowselect" : true,
23566 * Fires when a row is selected.
23567 * @param {SelectionModel} this
23568 * @param {Number} rowIndex The selected index
23569 * @param {Roo.data.Record} r The record
23571 "rowselect" : true,
23573 * @event rowdeselect
23574 * Fires when a row is deselected.
23575 * @param {SelectionModel} this
23576 * @param {Number} rowIndex The selected index
23578 "rowdeselect" : true
23580 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23581 this.locked = false;
23584 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
23586 * @cfg {Boolean} singleSelect
23587 * True to allow selection of only one row at a time (defaults to false)
23589 singleSelect : false,
23592 initEvents : function()
23595 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
23596 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
23597 //}else{ // allow click to work like normal
23598 // this.grid.on("rowclick", this.handleDragableRowClick, this);
23600 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
23601 this.grid.on("rowclick", this.handleMouseDown, this);
23603 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
23604 "up" : function(e){
23606 this.selectPrevious(e.shiftKey);
23607 }else if(this.last !== false && this.lastActive !== false){
23608 var last = this.last;
23609 this.selectRange(this.last, this.lastActive-1);
23610 this.grid.getView().focusRow(this.lastActive);
23611 if(last !== false){
23615 this.selectFirstRow();
23617 this.fireEvent("afterselectionchange", this);
23619 "down" : function(e){
23621 this.selectNext(e.shiftKey);
23622 }else if(this.last !== false && this.lastActive !== false){
23623 var last = this.last;
23624 this.selectRange(this.last, this.lastActive+1);
23625 this.grid.getView().focusRow(this.lastActive);
23626 if(last !== false){
23630 this.selectFirstRow();
23632 this.fireEvent("afterselectionchange", this);
23636 this.grid.store.on('load', function(){
23637 this.selections.clear();
23640 var view = this.grid.view;
23641 view.on("refresh", this.onRefresh, this);
23642 view.on("rowupdated", this.onRowUpdated, this);
23643 view.on("rowremoved", this.onRemove, this);
23648 onRefresh : function()
23650 var ds = this.grid.store, i, v = this.grid.view;
23651 var s = this.selections;
23652 s.each(function(r){
23653 if((i = ds.indexOfId(r.id)) != -1){
23662 onRemove : function(v, index, r){
23663 this.selections.remove(r);
23667 onRowUpdated : function(v, index, r){
23668 if(this.isSelected(r)){
23669 v.onRowSelect(index);
23675 * @param {Array} records The records to select
23676 * @param {Boolean} keepExisting (optional) True to keep existing selections
23678 selectRecords : function(records, keepExisting)
23681 this.clearSelections();
23683 var ds = this.grid.store;
23684 for(var i = 0, len = records.length; i < len; i++){
23685 this.selectRow(ds.indexOf(records[i]), true);
23690 * Gets the number of selected rows.
23693 getCount : function(){
23694 return this.selections.length;
23698 * Selects the first row in the grid.
23700 selectFirstRow : function(){
23705 * Select the last row.
23706 * @param {Boolean} keepExisting (optional) True to keep existing selections
23708 selectLastRow : function(keepExisting){
23709 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
23710 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
23714 * Selects the row immediately following the last selected row.
23715 * @param {Boolean} keepExisting (optional) True to keep existing selections
23717 selectNext : function(keepExisting)
23719 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
23720 this.selectRow(this.last+1, keepExisting);
23721 this.grid.getView().focusRow(this.last);
23726 * Selects the row that precedes the last selected row.
23727 * @param {Boolean} keepExisting (optional) True to keep existing selections
23729 selectPrevious : function(keepExisting){
23731 this.selectRow(this.last-1, keepExisting);
23732 this.grid.getView().focusRow(this.last);
23737 * Returns the selected records
23738 * @return {Array} Array of selected records
23740 getSelections : function(){
23741 return [].concat(this.selections.items);
23745 * Returns the first selected record.
23748 getSelected : function(){
23749 return this.selections.itemAt(0);
23754 * Clears all selections.
23756 clearSelections : function(fast)
23762 var ds = this.grid.store;
23763 var s = this.selections;
23764 s.each(function(r){
23765 this.deselectRow(ds.indexOfId(r.id));
23769 this.selections.clear();
23776 * Selects all rows.
23778 selectAll : function(){
23782 this.selections.clear();
23783 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
23784 this.selectRow(i, true);
23789 * Returns True if there is a selection.
23790 * @return {Boolean}
23792 hasSelection : function(){
23793 return this.selections.length > 0;
23797 * Returns True if the specified row is selected.
23798 * @param {Number/Record} record The record or index of the record to check
23799 * @return {Boolean}
23801 isSelected : function(index){
23802 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
23803 return (r && this.selections.key(r.id) ? true : false);
23807 * Returns True if the specified record id is selected.
23808 * @param {String} id The id of record to check
23809 * @return {Boolean}
23811 isIdSelected : function(id){
23812 return (this.selections.key(id) ? true : false);
23817 handleMouseDBClick : function(e, t){
23821 handleMouseDown : function(e, t)
23823 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
23824 if(this.isLocked() || rowIndex < 0 ){
23827 if(e.shiftKey && this.last !== false){
23828 var last = this.last;
23829 this.selectRange(last, rowIndex, e.ctrlKey);
23830 this.last = last; // reset the last
23834 var isSelected = this.isSelected(rowIndex);
23835 //Roo.log("select row:" + rowIndex);
23837 this.deselectRow(rowIndex);
23839 this.selectRow(rowIndex, true);
23843 if(e.button !== 0 && isSelected){
23844 alert('rowIndex 2: ' + rowIndex);
23845 view.focusRow(rowIndex);
23846 }else if(e.ctrlKey && isSelected){
23847 this.deselectRow(rowIndex);
23848 }else if(!isSelected){
23849 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
23850 view.focusRow(rowIndex);
23854 this.fireEvent("afterselectionchange", this);
23857 handleDragableRowClick : function(grid, rowIndex, e)
23859 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
23860 this.selectRow(rowIndex, false);
23861 grid.view.focusRow(rowIndex);
23862 this.fireEvent("afterselectionchange", this);
23867 * Selects multiple rows.
23868 * @param {Array} rows Array of the indexes of the row to select
23869 * @param {Boolean} keepExisting (optional) True to keep existing selections
23871 selectRows : function(rows, keepExisting){
23873 this.clearSelections();
23875 for(var i = 0, len = rows.length; i < len; i++){
23876 this.selectRow(rows[i], true);
23881 * Selects a range of rows. All rows in between startRow and endRow are also selected.
23882 * @param {Number} startRow The index of the first row in the range
23883 * @param {Number} endRow The index of the last row in the range
23884 * @param {Boolean} keepExisting (optional) True to retain existing selections
23886 selectRange : function(startRow, endRow, keepExisting){
23891 this.clearSelections();
23893 if(startRow <= endRow){
23894 for(var i = startRow; i <= endRow; i++){
23895 this.selectRow(i, true);
23898 for(var i = startRow; i >= endRow; i--){
23899 this.selectRow(i, true);
23905 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
23906 * @param {Number} startRow The index of the first row in the range
23907 * @param {Number} endRow The index of the last row in the range
23909 deselectRange : function(startRow, endRow, preventViewNotify){
23913 for(var i = startRow; i <= endRow; i++){
23914 this.deselectRow(i, preventViewNotify);
23920 * @param {Number} row The index of the row to select
23921 * @param {Boolean} keepExisting (optional) True to keep existing selections
23923 selectRow : function(index, keepExisting, preventViewNotify)
23925 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
23928 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
23929 if(!keepExisting || this.singleSelect){
23930 this.clearSelections();
23933 var r = this.grid.store.getAt(index);
23934 //console.log('selectRow - record id :' + r.id);
23936 this.selections.add(r);
23937 this.last = this.lastActive = index;
23938 if(!preventViewNotify){
23939 var proxy = new Roo.Element(
23940 this.grid.getRowDom(index)
23942 proxy.addClass('bg-info info');
23944 this.fireEvent("rowselect", this, index, r);
23945 this.fireEvent("selectionchange", this);
23951 * @param {Number} row The index of the row to deselect
23953 deselectRow : function(index, preventViewNotify)
23958 if(this.last == index){
23961 if(this.lastActive == index){
23962 this.lastActive = false;
23965 var r = this.grid.store.getAt(index);
23970 this.selections.remove(r);
23971 //.console.log('deselectRow - record id :' + r.id);
23972 if(!preventViewNotify){
23974 var proxy = new Roo.Element(
23975 this.grid.getRowDom(index)
23977 proxy.removeClass('bg-info info');
23979 this.fireEvent("rowdeselect", this, index);
23980 this.fireEvent("selectionchange", this);
23984 restoreLast : function(){
23986 this.last = this._last;
23991 acceptsNav : function(row, col, cm){
23992 return !cm.isHidden(col) && cm.isCellEditable(col, row);
23996 onEditorKey : function(field, e){
23997 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24002 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24004 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24006 }else if(k == e.ENTER && !e.ctrlKey){
24010 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24012 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24014 }else if(k == e.ESC){
24018 g.startEditing(newCell[0], newCell[1]);
24024 * Ext JS Library 1.1.1
24025 * Copyright(c) 2006-2007, Ext JS, LLC.
24027 * Originally Released Under LGPL - original licence link has changed is not relivant.
24030 * <script type="text/javascript">
24034 * @class Roo.bootstrap.PagingToolbar
24035 * @extends Roo.bootstrap.NavSimplebar
24036 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24038 * Create a new PagingToolbar
24039 * @param {Object} config The config object
24040 * @param {Roo.data.Store} store
24042 Roo.bootstrap.PagingToolbar = function(config)
24044 // old args format still supported... - xtype is prefered..
24045 // created from xtype...
24047 this.ds = config.dataSource;
24049 if (config.store && !this.ds) {
24050 this.store= Roo.factory(config.store, Roo.data);
24051 this.ds = this.store;
24052 this.ds.xmodule = this.xmodule || false;
24055 this.toolbarItems = [];
24056 if (config.items) {
24057 this.toolbarItems = config.items;
24060 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24065 this.bind(this.ds);
24068 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24072 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24074 * @cfg {Roo.data.Store} dataSource
24075 * The underlying data store providing the paged data
24078 * @cfg {String/HTMLElement/Element} container
24079 * container The id or element that will contain the toolbar
24082 * @cfg {Boolean} displayInfo
24083 * True to display the displayMsg (defaults to false)
24086 * @cfg {Number} pageSize
24087 * The number of records to display per page (defaults to 20)
24091 * @cfg {String} displayMsg
24092 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24094 displayMsg : 'Displaying {0} - {1} of {2}',
24096 * @cfg {String} emptyMsg
24097 * The message to display when no records are found (defaults to "No data to display")
24099 emptyMsg : 'No data to display',
24101 * Customizable piece of the default paging text (defaults to "Page")
24104 beforePageText : "Page",
24106 * Customizable piece of the default paging text (defaults to "of %0")
24109 afterPageText : "of {0}",
24111 * Customizable piece of the default paging text (defaults to "First Page")
24114 firstText : "First Page",
24116 * Customizable piece of the default paging text (defaults to "Previous Page")
24119 prevText : "Previous Page",
24121 * Customizable piece of the default paging text (defaults to "Next Page")
24124 nextText : "Next Page",
24126 * Customizable piece of the default paging text (defaults to "Last Page")
24129 lastText : "Last Page",
24131 * Customizable piece of the default paging text (defaults to "Refresh")
24134 refreshText : "Refresh",
24138 onRender : function(ct, position)
24140 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24141 this.navgroup.parentId = this.id;
24142 this.navgroup.onRender(this.el, null);
24143 // add the buttons to the navgroup
24145 if(this.displayInfo){
24146 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24147 this.displayEl = this.el.select('.x-paging-info', true).first();
24148 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24149 // this.displayEl = navel.el.select('span',true).first();
24155 Roo.each(_this.buttons, function(e){ // this might need to use render????
24156 Roo.factory(e).onRender(_this.el, null);
24160 Roo.each(_this.toolbarItems, function(e) {
24161 _this.navgroup.addItem(e);
24165 this.first = this.navgroup.addItem({
24166 tooltip: this.firstText,
24168 icon : 'fa fa-backward',
24170 preventDefault: true,
24171 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24174 this.prev = this.navgroup.addItem({
24175 tooltip: this.prevText,
24177 icon : 'fa fa-step-backward',
24179 preventDefault: true,
24180 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
24182 //this.addSeparator();
24185 var field = this.navgroup.addItem( {
24187 cls : 'x-paging-position',
24189 html : this.beforePageText +
24190 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24191 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
24194 this.field = field.el.select('input', true).first();
24195 this.field.on("keydown", this.onPagingKeydown, this);
24196 this.field.on("focus", function(){this.dom.select();});
24199 this.afterTextEl = field.el.select('.x-paging-after',true).first();
24200 //this.field.setHeight(18);
24201 //this.addSeparator();
24202 this.next = this.navgroup.addItem({
24203 tooltip: this.nextText,
24205 html : ' <i class="fa fa-step-forward">',
24207 preventDefault: true,
24208 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
24210 this.last = this.navgroup.addItem({
24211 tooltip: this.lastText,
24212 icon : 'fa fa-forward',
24215 preventDefault: true,
24216 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
24218 //this.addSeparator();
24219 this.loading = this.navgroup.addItem({
24220 tooltip: this.refreshText,
24221 icon: 'fa fa-refresh',
24222 preventDefault: true,
24223 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24229 updateInfo : function(){
24230 if(this.displayEl){
24231 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24232 var msg = count == 0 ?
24236 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
24238 this.displayEl.update(msg);
24243 onLoad : function(ds, r, o)
24245 this.cursor = o.params ? o.params.start : 0;
24246 var d = this.getPageData(),
24251 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24252 this.field.dom.value = ap;
24253 this.first.setDisabled(ap == 1);
24254 this.prev.setDisabled(ap == 1);
24255 this.next.setDisabled(ap == ps);
24256 this.last.setDisabled(ap == ps);
24257 this.loading.enable();
24262 getPageData : function(){
24263 var total = this.ds.getTotalCount();
24266 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24267 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24272 onLoadError : function(){
24273 this.loading.enable();
24277 onPagingKeydown : function(e){
24278 var k = e.getKey();
24279 var d = this.getPageData();
24281 var v = this.field.dom.value, pageNum;
24282 if(!v || isNaN(pageNum = parseInt(v, 10))){
24283 this.field.dom.value = d.activePage;
24286 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24287 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24290 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))
24292 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24293 this.field.dom.value = pageNum;
24294 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24297 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24299 var v = this.field.dom.value, pageNum;
24300 var increment = (e.shiftKey) ? 10 : 1;
24301 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24304 if(!v || isNaN(pageNum = parseInt(v, 10))) {
24305 this.field.dom.value = d.activePage;
24308 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24310 this.field.dom.value = parseInt(v, 10) + increment;
24311 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24312 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24319 beforeLoad : function(){
24321 this.loading.disable();
24326 onClick : function(which){
24335 ds.load({params:{start: 0, limit: this.pageSize}});
24338 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24341 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24344 var total = ds.getTotalCount();
24345 var extra = total % this.pageSize;
24346 var lastStart = extra ? (total - extra) : total-this.pageSize;
24347 ds.load({params:{start: lastStart, limit: this.pageSize}});
24350 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24356 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24357 * @param {Roo.data.Store} store The data store to unbind
24359 unbind : function(ds){
24360 ds.un("beforeload", this.beforeLoad, this);
24361 ds.un("load", this.onLoad, this);
24362 ds.un("loadexception", this.onLoadError, this);
24363 ds.un("remove", this.updateInfo, this);
24364 ds.un("add", this.updateInfo, this);
24365 this.ds = undefined;
24369 * Binds the paging toolbar to the specified {@link Roo.data.Store}
24370 * @param {Roo.data.Store} store The data store to bind
24372 bind : function(ds){
24373 ds.on("beforeload", this.beforeLoad, this);
24374 ds.on("load", this.onLoad, this);
24375 ds.on("loadexception", this.onLoadError, this);
24376 ds.on("remove", this.updateInfo, this);
24377 ds.on("add", this.updateInfo, this);
24388 * @class Roo.bootstrap.MessageBar
24389 * @extends Roo.bootstrap.Component
24390 * Bootstrap MessageBar class
24391 * @cfg {String} html contents of the MessageBar
24392 * @cfg {String} weight (info | success | warning | danger) default info
24393 * @cfg {String} beforeClass insert the bar before the given class
24394 * @cfg {Boolean} closable (true | false) default false
24395 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24398 * Create a new Element
24399 * @param {Object} config The config object
24402 Roo.bootstrap.MessageBar = function(config){
24403 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24406 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
24412 beforeClass: 'bootstrap-sticky-wrap',
24414 getAutoCreate : function(){
24418 cls: 'alert alert-dismissable alert-' + this.weight,
24423 html: this.html || ''
24429 cfg.cls += ' alert-messages-fixed';
24443 onRender : function(ct, position)
24445 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24448 var cfg = Roo.apply({}, this.getAutoCreate());
24452 cfg.cls += ' ' + this.cls;
24455 cfg.style = this.style;
24457 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24459 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24462 this.el.select('>button.close').on('click', this.hide, this);
24468 if (!this.rendered) {
24474 this.fireEvent('show', this);
24480 if (!this.rendered) {
24486 this.fireEvent('hide', this);
24489 update : function()
24491 // var e = this.el.dom.firstChild;
24493 // if(this.closable){
24494 // e = e.nextSibling;
24497 // e.data = this.html || '';
24499 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24515 * @class Roo.bootstrap.Graph
24516 * @extends Roo.bootstrap.Component
24517 * Bootstrap Graph class
24521 @cfg {String} graphtype bar | vbar | pie
24522 @cfg {number} g_x coodinator | centre x (pie)
24523 @cfg {number} g_y coodinator | centre y (pie)
24524 @cfg {number} g_r radius (pie)
24525 @cfg {number} g_height height of the chart (respected by all elements in the set)
24526 @cfg {number} g_width width of the chart (respected by all elements in the set)
24527 @cfg {Object} title The title of the chart
24530 -opts (object) options for the chart
24532 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24533 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24535 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.
24536 o stacked (boolean) whether or not to tread values as in a stacked bar chart
24538 o stretch (boolean)
24540 -opts (object) options for the pie
24543 o startAngle (number)
24544 o endAngle (number)
24548 * Create a new Input
24549 * @param {Object} config The config object
24552 Roo.bootstrap.Graph = function(config){
24553 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24559 * The img click event for the img.
24560 * @param {Roo.EventObject} e
24566 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
24577 //g_colors: this.colors,
24584 getAutoCreate : function(){
24595 onRender : function(ct,position){
24598 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
24600 if (typeof(Raphael) == 'undefined') {
24601 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
24605 this.raphael = Raphael(this.el.dom);
24607 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24608 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24609 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24610 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
24612 r.text(160, 10, "Single Series Chart").attr(txtattr);
24613 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
24614 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
24615 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
24617 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
24618 r.barchart(330, 10, 300, 220, data1);
24619 r.barchart(10, 250, 300, 220, data2, {stacked: true});
24620 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
24623 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24624 // r.barchart(30, 30, 560, 250, xdata, {
24625 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
24626 // axis : "0 0 1 1",
24627 // axisxlabels : xdata
24628 // //yvalues : cols,
24631 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24633 // this.load(null,xdata,{
24634 // axis : "0 0 1 1",
24635 // axisxlabels : xdata
24640 load : function(graphtype,xdata,opts)
24642 this.raphael.clear();
24644 graphtype = this.graphtype;
24649 var r = this.raphael,
24650 fin = function () {
24651 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
24653 fout = function () {
24654 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
24656 pfin = function() {
24657 this.sector.stop();
24658 this.sector.scale(1.1, 1.1, this.cx, this.cy);
24661 this.label[0].stop();
24662 this.label[0].attr({ r: 7.5 });
24663 this.label[1].attr({ "font-weight": 800 });
24666 pfout = function() {
24667 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
24670 this.label[0].animate({ r: 5 }, 500, "bounce");
24671 this.label[1].attr({ "font-weight": 400 });
24677 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24680 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24683 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
24684 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
24686 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
24693 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
24698 setTitle: function(o)
24703 initEvents: function() {
24706 this.el.on('click', this.onClick, this);
24710 onClick : function(e)
24712 Roo.log('img onclick');
24713 this.fireEvent('click', this, e);
24725 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24728 * @class Roo.bootstrap.dash.NumberBox
24729 * @extends Roo.bootstrap.Component
24730 * Bootstrap NumberBox class
24731 * @cfg {String} headline Box headline
24732 * @cfg {String} content Box content
24733 * @cfg {String} icon Box icon
24734 * @cfg {String} footer Footer text
24735 * @cfg {String} fhref Footer href
24738 * Create a new NumberBox
24739 * @param {Object} config The config object
24743 Roo.bootstrap.dash.NumberBox = function(config){
24744 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
24748 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
24757 getAutoCreate : function(){
24761 cls : 'small-box ',
24769 cls : 'roo-headline',
24770 html : this.headline
24774 cls : 'roo-content',
24775 html : this.content
24789 cls : 'ion ' + this.icon
24798 cls : 'small-box-footer',
24799 href : this.fhref || '#',
24803 cfg.cn.push(footer);
24810 onRender : function(ct,position){
24811 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
24818 setHeadline: function (value)
24820 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
24823 setFooter: function (value, href)
24825 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
24828 this.el.select('a.small-box-footer',true).first().attr('href', href);
24833 setContent: function (value)
24835 this.el.select('.roo-content',true).first().dom.innerHTML = value;
24838 initEvents: function()
24852 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24855 * @class Roo.bootstrap.dash.TabBox
24856 * @extends Roo.bootstrap.Component
24857 * Bootstrap TabBox class
24858 * @cfg {String} title Title of the TabBox
24859 * @cfg {String} icon Icon of the TabBox
24860 * @cfg {Boolean} showtabs (true|false) show the tabs default true
24861 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
24864 * Create a new TabBox
24865 * @param {Object} config The config object
24869 Roo.bootstrap.dash.TabBox = function(config){
24870 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
24875 * When a pane is added
24876 * @param {Roo.bootstrap.dash.TabPane} pane
24880 * @event activatepane
24881 * When a pane is activated
24882 * @param {Roo.bootstrap.dash.TabPane} pane
24884 "activatepane" : true
24892 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
24897 tabScrollable : false,
24899 getChildContainer : function()
24901 return this.el.select('.tab-content', true).first();
24904 getAutoCreate : function(){
24908 cls: 'pull-left header',
24916 cls: 'fa ' + this.icon
24922 cls: 'nav nav-tabs pull-right',
24928 if(this.tabScrollable){
24935 cls: 'nav nav-tabs pull-right',
24946 cls: 'nav-tabs-custom',
24951 cls: 'tab-content no-padding',
24959 initEvents : function()
24961 //Roo.log('add add pane handler');
24962 this.on('addpane', this.onAddPane, this);
24965 * Updates the box title
24966 * @param {String} html to set the title to.
24968 setTitle : function(value)
24970 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
24972 onAddPane : function(pane)
24974 this.panes.push(pane);
24975 //Roo.log('addpane');
24977 // tabs are rendere left to right..
24978 if(!this.showtabs){
24982 var ctr = this.el.select('.nav-tabs', true).first();
24985 var existing = ctr.select('.nav-tab',true);
24986 var qty = existing.getCount();;
24989 var tab = ctr.createChild({
24991 cls : 'nav-tab' + (qty ? '' : ' active'),
24999 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25002 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25004 pane.el.addClass('active');
25009 onTabClick : function(ev,un,ob,pane)
25011 //Roo.log('tab - prev default');
25012 ev.preventDefault();
25015 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25016 pane.tab.addClass('active');
25017 //Roo.log(pane.title);
25018 this.getChildContainer().select('.tab-pane',true).removeClass('active');
25019 // technically we should have a deactivate event.. but maybe add later.
25020 // and it should not de-activate the selected tab...
25021 this.fireEvent('activatepane', pane);
25022 pane.el.addClass('active');
25023 pane.fireEvent('activate');
25028 getActivePane : function()
25031 Roo.each(this.panes, function(p) {
25032 if(p.el.hasClass('active')){
25053 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25055 * @class Roo.bootstrap.TabPane
25056 * @extends Roo.bootstrap.Component
25057 * Bootstrap TabPane class
25058 * @cfg {Boolean} active (false | true) Default false
25059 * @cfg {String} title title of panel
25063 * Create a new TabPane
25064 * @param {Object} config The config object
25067 Roo.bootstrap.dash.TabPane = function(config){
25068 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25074 * When a pane is activated
25075 * @param {Roo.bootstrap.dash.TabPane} pane
25082 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
25087 // the tabBox that this is attached to.
25090 getAutoCreate : function()
25098 cfg.cls += ' active';
25103 initEvents : function()
25105 //Roo.log('trigger add pane handler');
25106 this.parent().fireEvent('addpane', this)
25110 * Updates the tab title
25111 * @param {String} html to set the title to.
25113 setTitle: function(str)
25119 this.tab.select('a', true).first().dom.innerHTML = str;
25136 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25139 * @class Roo.bootstrap.menu.Menu
25140 * @extends Roo.bootstrap.Component
25141 * Bootstrap Menu class - container for Menu
25142 * @cfg {String} html Text of the menu
25143 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25144 * @cfg {String} icon Font awesome icon
25145 * @cfg {String} pos Menu align to (top | bottom) default bottom
25149 * Create a new Menu
25150 * @param {Object} config The config object
25154 Roo.bootstrap.menu.Menu = function(config){
25155 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25159 * @event beforeshow
25160 * Fires before this menu is displayed
25161 * @param {Roo.bootstrap.menu.Menu} this
25165 * @event beforehide
25166 * Fires before this menu is hidden
25167 * @param {Roo.bootstrap.menu.Menu} this
25172 * Fires after this menu is displayed
25173 * @param {Roo.bootstrap.menu.Menu} this
25178 * Fires after this menu is hidden
25179 * @param {Roo.bootstrap.menu.Menu} this
25184 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25185 * @param {Roo.bootstrap.menu.Menu} this
25186 * @param {Roo.EventObject} e
25193 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
25197 weight : 'default',
25202 getChildContainer : function() {
25203 if(this.isSubMenu){
25207 return this.el.select('ul.dropdown-menu', true).first();
25210 getAutoCreate : function()
25215 cls : 'roo-menu-text',
25223 cls : 'fa ' + this.icon
25234 cls : 'dropdown-button btn btn-' + this.weight,
25239 cls : 'dropdown-toggle btn btn-' + this.weight,
25249 cls : 'dropdown-menu'
25255 if(this.pos == 'top'){
25256 cfg.cls += ' dropup';
25259 if(this.isSubMenu){
25262 cls : 'dropdown-menu'
25269 onRender : function(ct, position)
25271 this.isSubMenu = ct.hasClass('dropdown-submenu');
25273 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25276 initEvents : function()
25278 if(this.isSubMenu){
25282 this.hidden = true;
25284 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25285 this.triggerEl.on('click', this.onTriggerPress, this);
25287 this.buttonEl = this.el.select('button.dropdown-button', true).first();
25288 this.buttonEl.on('click', this.onClick, this);
25294 if(this.isSubMenu){
25298 return this.el.select('ul.dropdown-menu', true).first();
25301 onClick : function(e)
25303 this.fireEvent("click", this, e);
25306 onTriggerPress : function(e)
25308 if (this.isVisible()) {
25315 isVisible : function(){
25316 return !this.hidden;
25321 this.fireEvent("beforeshow", this);
25323 this.hidden = false;
25324 this.el.addClass('open');
25326 Roo.get(document).on("mouseup", this.onMouseUp, this);
25328 this.fireEvent("show", this);
25335 this.fireEvent("beforehide", this);
25337 this.hidden = true;
25338 this.el.removeClass('open');
25340 Roo.get(document).un("mouseup", this.onMouseUp);
25342 this.fireEvent("hide", this);
25345 onMouseUp : function()
25359 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25362 * @class Roo.bootstrap.menu.Item
25363 * @extends Roo.bootstrap.Component
25364 * Bootstrap MenuItem class
25365 * @cfg {Boolean} submenu (true | false) default false
25366 * @cfg {String} html text of the item
25367 * @cfg {String} href the link
25368 * @cfg {Boolean} disable (true | false) default false
25369 * @cfg {Boolean} preventDefault (true | false) default true
25370 * @cfg {String} icon Font awesome icon
25371 * @cfg {String} pos Submenu align to (left | right) default right
25375 * Create a new Item
25376 * @param {Object} config The config object
25380 Roo.bootstrap.menu.Item = function(config){
25381 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25385 * Fires when the mouse is hovering over this menu
25386 * @param {Roo.bootstrap.menu.Item} this
25387 * @param {Roo.EventObject} e
25392 * Fires when the mouse exits this menu
25393 * @param {Roo.bootstrap.menu.Item} this
25394 * @param {Roo.EventObject} e
25400 * The raw click event for the entire grid.
25401 * @param {Roo.EventObject} e
25407 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
25412 preventDefault: true,
25417 getAutoCreate : function()
25422 cls : 'roo-menu-item-text',
25430 cls : 'fa ' + this.icon
25439 href : this.href || '#',
25446 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25450 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25452 if(this.pos == 'left'){
25453 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25460 initEvents : function()
25462 this.el.on('mouseover', this.onMouseOver, this);
25463 this.el.on('mouseout', this.onMouseOut, this);
25465 this.el.select('a', true).first().on('click', this.onClick, this);
25469 onClick : function(e)
25471 if(this.preventDefault){
25472 e.preventDefault();
25475 this.fireEvent("click", this, e);
25478 onMouseOver : function(e)
25480 if(this.submenu && this.pos == 'left'){
25481 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25484 this.fireEvent("mouseover", this, e);
25487 onMouseOut : function(e)
25489 this.fireEvent("mouseout", this, e);
25501 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25504 * @class Roo.bootstrap.menu.Separator
25505 * @extends Roo.bootstrap.Component
25506 * Bootstrap Separator class
25509 * Create a new Separator
25510 * @param {Object} config The config object
25514 Roo.bootstrap.menu.Separator = function(config){
25515 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25518 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
25520 getAutoCreate : function(){
25541 * @class Roo.bootstrap.Tooltip
25542 * Bootstrap Tooltip class
25543 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25544 * to determine which dom element triggers the tooltip.
25546 * It needs to add support for additional attributes like tooltip-position
25549 * Create a new Toolti
25550 * @param {Object} config The config object
25553 Roo.bootstrap.Tooltip = function(config){
25554 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25556 this.alignment = Roo.bootstrap.Tooltip.alignment;
25558 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25559 this.alignment = config.alignment;
25564 Roo.apply(Roo.bootstrap.Tooltip, {
25566 * @function init initialize tooltip monitoring.
25570 currentTip : false,
25571 currentRegion : false,
25577 Roo.get(document).on('mouseover', this.enter ,this);
25578 Roo.get(document).on('mouseout', this.leave, this);
25581 this.currentTip = new Roo.bootstrap.Tooltip();
25584 enter : function(ev)
25586 var dom = ev.getTarget();
25588 //Roo.log(['enter',dom]);
25589 var el = Roo.fly(dom);
25590 if (this.currentEl) {
25592 //Roo.log(this.currentEl);
25593 //Roo.log(this.currentEl.contains(dom));
25594 if (this.currentEl == el) {
25597 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
25603 if (this.currentTip.el) {
25604 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
25608 if(!el || el.dom == document){
25614 // you can not look for children, as if el is the body.. then everythign is the child..
25615 if (!el.attr('tooltip')) { //
25616 if (!el.select("[tooltip]").elements.length) {
25619 // is the mouse over this child...?
25620 bindEl = el.select("[tooltip]").first();
25621 var xy = ev.getXY();
25622 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
25623 //Roo.log("not in region.");
25626 //Roo.log("child element over..");
25629 this.currentEl = bindEl;
25630 this.currentTip.bind(bindEl);
25631 this.currentRegion = Roo.lib.Region.getRegion(dom);
25632 this.currentTip.enter();
25635 leave : function(ev)
25637 var dom = ev.getTarget();
25638 //Roo.log(['leave',dom]);
25639 if (!this.currentEl) {
25644 if (dom != this.currentEl.dom) {
25647 var xy = ev.getXY();
25648 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
25651 // only activate leave if mouse cursor is outside... bounding box..
25656 if (this.currentTip) {
25657 this.currentTip.leave();
25659 //Roo.log('clear currentEl');
25660 this.currentEl = false;
25665 'left' : ['r-l', [-2,0], 'right'],
25666 'right' : ['l-r', [2,0], 'left'],
25667 'bottom' : ['t-b', [0,2], 'top'],
25668 'top' : [ 'b-t', [0,-2], 'bottom']
25674 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
25679 delay : null, // can be { show : 300 , hide: 500}
25683 hoverState : null, //???
25685 placement : 'bottom',
25689 getAutoCreate : function(){
25696 cls : 'tooltip-arrow'
25699 cls : 'tooltip-inner'
25706 bind : function(el)
25712 enter : function () {
25714 if (this.timeout != null) {
25715 clearTimeout(this.timeout);
25718 this.hoverState = 'in';
25719 //Roo.log("enter - show");
25720 if (!this.delay || !this.delay.show) {
25725 this.timeout = setTimeout(function () {
25726 if (_t.hoverState == 'in') {
25729 }, this.delay.show);
25733 clearTimeout(this.timeout);
25735 this.hoverState = 'out';
25736 if (!this.delay || !this.delay.hide) {
25742 this.timeout = setTimeout(function () {
25743 //Roo.log("leave - timeout");
25745 if (_t.hoverState == 'out') {
25747 Roo.bootstrap.Tooltip.currentEl = false;
25752 show : function (msg)
25755 this.render(document.body);
25758 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
25760 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
25762 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
25764 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
25766 var placement = typeof this.placement == 'function' ?
25767 this.placement.call(this, this.el, on_el) :
25770 var autoToken = /\s?auto?\s?/i;
25771 var autoPlace = autoToken.test(placement);
25773 placement = placement.replace(autoToken, '') || 'top';
25777 //this.el.setXY([0,0]);
25779 //this.el.dom.style.display='block';
25781 //this.el.appendTo(on_el);
25783 var p = this.getPosition();
25784 var box = this.el.getBox();
25790 var align = this.alignment[placement];
25792 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
25794 if(placement == 'top' || placement == 'bottom'){
25796 placement = 'right';
25799 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
25800 placement = 'left';
25803 var scroll = Roo.select('body', true).first().getScroll();
25805 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
25811 this.el.alignTo(this.bindEl, align[0],align[1]);
25812 //var arrow = this.el.select('.arrow',true).first();
25813 //arrow.set(align[2],
25815 this.el.addClass(placement);
25817 this.el.addClass('in fade');
25819 this.hoverState = null;
25821 if (this.el.hasClass('fade')) {
25832 //this.el.setXY([0,0]);
25833 this.el.removeClass('in');
25849 * @class Roo.bootstrap.LocationPicker
25850 * @extends Roo.bootstrap.Component
25851 * Bootstrap LocationPicker class
25852 * @cfg {Number} latitude Position when init default 0
25853 * @cfg {Number} longitude Position when init default 0
25854 * @cfg {Number} zoom default 15
25855 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
25856 * @cfg {Boolean} mapTypeControl default false
25857 * @cfg {Boolean} disableDoubleClickZoom default false
25858 * @cfg {Boolean} scrollwheel default true
25859 * @cfg {Boolean} streetViewControl default false
25860 * @cfg {Number} radius default 0
25861 * @cfg {String} locationName
25862 * @cfg {Boolean} draggable default true
25863 * @cfg {Boolean} enableAutocomplete default false
25864 * @cfg {Boolean} enableReverseGeocode default true
25865 * @cfg {String} markerTitle
25868 * Create a new LocationPicker
25869 * @param {Object} config The config object
25873 Roo.bootstrap.LocationPicker = function(config){
25875 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
25880 * Fires when the picker initialized.
25881 * @param {Roo.bootstrap.LocationPicker} this
25882 * @param {Google Location} location
25886 * @event positionchanged
25887 * Fires when the picker position changed.
25888 * @param {Roo.bootstrap.LocationPicker} this
25889 * @param {Google Location} location
25891 positionchanged : true,
25894 * Fires when the map resize.
25895 * @param {Roo.bootstrap.LocationPicker} this
25900 * Fires when the map show.
25901 * @param {Roo.bootstrap.LocationPicker} this
25906 * Fires when the map hide.
25907 * @param {Roo.bootstrap.LocationPicker} this
25912 * Fires when click the map.
25913 * @param {Roo.bootstrap.LocationPicker} this
25914 * @param {Map event} e
25918 * @event mapRightClick
25919 * Fires when right click the map.
25920 * @param {Roo.bootstrap.LocationPicker} this
25921 * @param {Map event} e
25923 mapRightClick : true,
25925 * @event markerClick
25926 * Fires when click the marker.
25927 * @param {Roo.bootstrap.LocationPicker} this
25928 * @param {Map event} e
25930 markerClick : true,
25932 * @event markerRightClick
25933 * Fires when right click the marker.
25934 * @param {Roo.bootstrap.LocationPicker} this
25935 * @param {Map event} e
25937 markerRightClick : true,
25939 * @event OverlayViewDraw
25940 * Fires when OverlayView Draw
25941 * @param {Roo.bootstrap.LocationPicker} this
25943 OverlayViewDraw : true,
25945 * @event OverlayViewOnAdd
25946 * Fires when OverlayView Draw
25947 * @param {Roo.bootstrap.LocationPicker} this
25949 OverlayViewOnAdd : true,
25951 * @event OverlayViewOnRemove
25952 * Fires when OverlayView Draw
25953 * @param {Roo.bootstrap.LocationPicker} this
25955 OverlayViewOnRemove : true,
25957 * @event OverlayViewShow
25958 * Fires when OverlayView Draw
25959 * @param {Roo.bootstrap.LocationPicker} this
25960 * @param {Pixel} cpx
25962 OverlayViewShow : true,
25964 * @event OverlayViewHide
25965 * Fires when OverlayView Draw
25966 * @param {Roo.bootstrap.LocationPicker} this
25968 OverlayViewHide : true,
25970 * @event loadexception
25971 * Fires when load google lib failed.
25972 * @param {Roo.bootstrap.LocationPicker} this
25974 loadexception : true
25979 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
25981 gMapContext: false,
25987 mapTypeControl: false,
25988 disableDoubleClickZoom: false,
25990 streetViewControl: false,
25994 enableAutocomplete: false,
25995 enableReverseGeocode: true,
25998 getAutoCreate: function()
26003 cls: 'roo-location-picker'
26009 initEvents: function(ct, position)
26011 if(!this.el.getWidth() || this.isApplied()){
26015 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26020 initial: function()
26022 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26023 this.fireEvent('loadexception', this);
26027 if(!this.mapTypeId){
26028 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26031 this.gMapContext = this.GMapContext();
26033 this.initOverlayView();
26035 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26039 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26040 _this.setPosition(_this.gMapContext.marker.position);
26043 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26044 _this.fireEvent('mapClick', this, event);
26048 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26049 _this.fireEvent('mapRightClick', this, event);
26053 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26054 _this.fireEvent('markerClick', this, event);
26058 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26059 _this.fireEvent('markerRightClick', this, event);
26063 this.setPosition(this.gMapContext.location);
26065 this.fireEvent('initial', this, this.gMapContext.location);
26068 initOverlayView: function()
26072 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26076 _this.fireEvent('OverlayViewDraw', _this);
26081 _this.fireEvent('OverlayViewOnAdd', _this);
26084 onRemove: function()
26086 _this.fireEvent('OverlayViewOnRemove', _this);
26089 show: function(cpx)
26091 _this.fireEvent('OverlayViewShow', _this, cpx);
26096 _this.fireEvent('OverlayViewHide', _this);
26102 fromLatLngToContainerPixel: function(event)
26104 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26107 isApplied: function()
26109 return this.getGmapContext() == false ? false : true;
26112 getGmapContext: function()
26114 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26117 GMapContext: function()
26119 var position = new google.maps.LatLng(this.latitude, this.longitude);
26121 var _map = new google.maps.Map(this.el.dom, {
26124 mapTypeId: this.mapTypeId,
26125 mapTypeControl: this.mapTypeControl,
26126 disableDoubleClickZoom: this.disableDoubleClickZoom,
26127 scrollwheel: this.scrollwheel,
26128 streetViewControl: this.streetViewControl,
26129 locationName: this.locationName,
26130 draggable: this.draggable,
26131 enableAutocomplete: this.enableAutocomplete,
26132 enableReverseGeocode: this.enableReverseGeocode
26135 var _marker = new google.maps.Marker({
26136 position: position,
26138 title: this.markerTitle,
26139 draggable: this.draggable
26146 location: position,
26147 radius: this.radius,
26148 locationName: this.locationName,
26149 addressComponents: {
26150 formatted_address: null,
26151 addressLine1: null,
26152 addressLine2: null,
26154 streetNumber: null,
26158 stateOrProvince: null
26161 domContainer: this.el.dom,
26162 geodecoder: new google.maps.Geocoder()
26166 drawCircle: function(center, radius, options)
26168 if (this.gMapContext.circle != null) {
26169 this.gMapContext.circle.setMap(null);
26173 options = Roo.apply({}, options, {
26174 strokeColor: "#0000FF",
26175 strokeOpacity: .35,
26177 fillColor: "#0000FF",
26181 options.map = this.gMapContext.map;
26182 options.radius = radius;
26183 options.center = center;
26184 this.gMapContext.circle = new google.maps.Circle(options);
26185 return this.gMapContext.circle;
26191 setPosition: function(location)
26193 this.gMapContext.location = location;
26194 this.gMapContext.marker.setPosition(location);
26195 this.gMapContext.map.panTo(location);
26196 this.drawCircle(location, this.gMapContext.radius, {});
26200 if (this.gMapContext.settings.enableReverseGeocode) {
26201 this.gMapContext.geodecoder.geocode({
26202 latLng: this.gMapContext.location
26203 }, function(results, status) {
26205 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26206 _this.gMapContext.locationName = results[0].formatted_address;
26207 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26209 _this.fireEvent('positionchanged', this, location);
26216 this.fireEvent('positionchanged', this, location);
26221 google.maps.event.trigger(this.gMapContext.map, "resize");
26223 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26225 this.fireEvent('resize', this);
26228 setPositionByLatLng: function(latitude, longitude)
26230 this.setPosition(new google.maps.LatLng(latitude, longitude));
26233 getCurrentPosition: function()
26236 latitude: this.gMapContext.location.lat(),
26237 longitude: this.gMapContext.location.lng()
26241 getAddressName: function()
26243 return this.gMapContext.locationName;
26246 getAddressComponents: function()
26248 return this.gMapContext.addressComponents;
26251 address_component_from_google_geocode: function(address_components)
26255 for (var i = 0; i < address_components.length; i++) {
26256 var component = address_components[i];
26257 if (component.types.indexOf("postal_code") >= 0) {
26258 result.postalCode = component.short_name;
26259 } else if (component.types.indexOf("street_number") >= 0) {
26260 result.streetNumber = component.short_name;
26261 } else if (component.types.indexOf("route") >= 0) {
26262 result.streetName = component.short_name;
26263 } else if (component.types.indexOf("neighborhood") >= 0) {
26264 result.city = component.short_name;
26265 } else if (component.types.indexOf("locality") >= 0) {
26266 result.city = component.short_name;
26267 } else if (component.types.indexOf("sublocality") >= 0) {
26268 result.district = component.short_name;
26269 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26270 result.stateOrProvince = component.short_name;
26271 } else if (component.types.indexOf("country") >= 0) {
26272 result.country = component.short_name;
26276 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26277 result.addressLine2 = "";
26281 setZoomLevel: function(zoom)
26283 this.gMapContext.map.setZoom(zoom);
26296 this.fireEvent('show', this);
26307 this.fireEvent('hide', this);
26312 Roo.apply(Roo.bootstrap.LocationPicker, {
26314 OverlayView : function(map, options)
26316 options = options || {};
26330 * @class Roo.bootstrap.Alert
26331 * @extends Roo.bootstrap.Component
26332 * Bootstrap Alert class
26333 * @cfg {String} title The title of alert
26334 * @cfg {String} html The content of alert
26335 * @cfg {String} weight ( success | info | warning | danger )
26336 * @cfg {String} faicon font-awesomeicon
26339 * Create a new alert
26340 * @param {Object} config The config object
26344 Roo.bootstrap.Alert = function(config){
26345 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26349 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
26356 getAutoCreate : function()
26365 cls : 'roo-alert-icon'
26370 cls : 'roo-alert-title',
26375 cls : 'roo-alert-text',
26382 cfg.cn[0].cls += ' fa ' + this.faicon;
26386 cfg.cls += ' alert-' + this.weight;
26392 initEvents: function()
26394 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26397 setTitle : function(str)
26399 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26402 setText : function(str)
26404 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26407 setWeight : function(weight)
26410 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26413 this.weight = weight;
26415 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26418 setIcon : function(icon)
26421 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26424 this.faicon = icon;
26426 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26447 * @class Roo.bootstrap.UploadCropbox
26448 * @extends Roo.bootstrap.Component
26449 * Bootstrap UploadCropbox class
26450 * @cfg {String} emptyText show when image has been loaded
26451 * @cfg {String} rotateNotify show when image too small to rotate
26452 * @cfg {Number} errorTimeout default 3000
26453 * @cfg {Number} minWidth default 300
26454 * @cfg {Number} minHeight default 300
26455 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26456 * @cfg {Boolean} isDocument (true|false) default false
26457 * @cfg {String} url action url
26458 * @cfg {String} paramName default 'imageUpload'
26459 * @cfg {String} method default POST
26460 * @cfg {Boolean} loadMask (true|false) default true
26461 * @cfg {Boolean} loadingText default 'Loading...'
26464 * Create a new UploadCropbox
26465 * @param {Object} config The config object
26468 Roo.bootstrap.UploadCropbox = function(config){
26469 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26473 * @event beforeselectfile
26474 * Fire before select file
26475 * @param {Roo.bootstrap.UploadCropbox} this
26477 "beforeselectfile" : true,
26480 * Fire after initEvent
26481 * @param {Roo.bootstrap.UploadCropbox} this
26486 * Fire after initEvent
26487 * @param {Roo.bootstrap.UploadCropbox} this
26488 * @param {String} data
26493 * Fire when preparing the file data
26494 * @param {Roo.bootstrap.UploadCropbox} this
26495 * @param {Object} file
26500 * Fire when get exception
26501 * @param {Roo.bootstrap.UploadCropbox} this
26502 * @param {XMLHttpRequest} xhr
26504 "exception" : true,
26506 * @event beforeloadcanvas
26507 * Fire before load the canvas
26508 * @param {Roo.bootstrap.UploadCropbox} this
26509 * @param {String} src
26511 "beforeloadcanvas" : true,
26514 * Fire when trash image
26515 * @param {Roo.bootstrap.UploadCropbox} this
26520 * Fire when download the image
26521 * @param {Roo.bootstrap.UploadCropbox} this
26525 * @event footerbuttonclick
26526 * Fire when footerbuttonclick
26527 * @param {Roo.bootstrap.UploadCropbox} this
26528 * @param {String} type
26530 "footerbuttonclick" : true,
26534 * @param {Roo.bootstrap.UploadCropbox} this
26539 * Fire when rotate the image
26540 * @param {Roo.bootstrap.UploadCropbox} this
26541 * @param {String} pos
26546 * Fire when inspect the file
26547 * @param {Roo.bootstrap.UploadCropbox} this
26548 * @param {Object} file
26553 * Fire when xhr upload the file
26554 * @param {Roo.bootstrap.UploadCropbox} this
26555 * @param {Object} data
26560 * Fire when arrange the file data
26561 * @param {Roo.bootstrap.UploadCropbox} this
26562 * @param {Object} formData
26567 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26570 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
26572 emptyText : 'Click to upload image',
26573 rotateNotify : 'Image is too small to rotate',
26574 errorTimeout : 3000,
26588 cropType : 'image/jpeg',
26590 canvasLoaded : false,
26591 isDocument : false,
26593 paramName : 'imageUpload',
26595 loadingText : 'Loading...',
26598 getAutoCreate : function()
26602 cls : 'roo-upload-cropbox',
26606 cls : 'roo-upload-cropbox-selector',
26611 cls : 'roo-upload-cropbox-body',
26612 style : 'cursor:pointer',
26616 cls : 'roo-upload-cropbox-preview'
26620 cls : 'roo-upload-cropbox-thumb'
26624 cls : 'roo-upload-cropbox-empty-notify',
26625 html : this.emptyText
26629 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
26630 html : this.rotateNotify
26636 cls : 'roo-upload-cropbox-footer',
26639 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
26649 onRender : function(ct, position)
26651 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
26653 if (this.buttons.length) {
26655 Roo.each(this.buttons, function(bb) {
26657 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
26659 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
26665 this.maskEl = this.el;
26669 initEvents : function()
26671 this.urlAPI = (window.createObjectURL && window) ||
26672 (window.URL && URL.revokeObjectURL && URL) ||
26673 (window.webkitURL && webkitURL);
26675 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
26676 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26678 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
26679 this.selectorEl.hide();
26681 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
26682 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26684 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
26685 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26686 this.thumbEl.hide();
26688 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
26689 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26691 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
26692 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26693 this.errorEl.hide();
26695 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
26696 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26697 this.footerEl.hide();
26699 this.setThumbBoxSize();
26705 this.fireEvent('initial', this);
26712 window.addEventListener("resize", function() { _this.resize(); } );
26714 this.bodyEl.on('click', this.beforeSelectFile, this);
26717 this.bodyEl.on('touchstart', this.onTouchStart, this);
26718 this.bodyEl.on('touchmove', this.onTouchMove, this);
26719 this.bodyEl.on('touchend', this.onTouchEnd, this);
26723 this.bodyEl.on('mousedown', this.onMouseDown, this);
26724 this.bodyEl.on('mousemove', this.onMouseMove, this);
26725 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
26726 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
26727 Roo.get(document).on('mouseup', this.onMouseUp, this);
26730 this.selectorEl.on('change', this.onFileSelected, this);
26736 this.baseScale = 1;
26738 this.baseRotate = 1;
26739 this.dragable = false;
26740 this.pinching = false;
26743 this.cropData = false;
26744 this.notifyEl.dom.innerHTML = this.emptyText;
26746 this.selectorEl.dom.value = '';
26750 resize : function()
26752 if(this.fireEvent('resize', this) != false){
26753 this.setThumbBoxPosition();
26754 this.setCanvasPosition();
26758 onFooterButtonClick : function(e, el, o, type)
26761 case 'rotate-left' :
26762 this.onRotateLeft(e);
26764 case 'rotate-right' :
26765 this.onRotateRight(e);
26768 this.beforeSelectFile(e);
26783 this.fireEvent('footerbuttonclick', this, type);
26786 beforeSelectFile : function(e)
26788 e.preventDefault();
26790 if(this.fireEvent('beforeselectfile', this) != false){
26791 this.selectorEl.dom.click();
26795 onFileSelected : function(e)
26797 e.preventDefault();
26799 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26803 var file = this.selectorEl.dom.files[0];
26805 if(this.fireEvent('inspect', this, file) != false){
26806 this.prepare(file);
26811 trash : function(e)
26813 this.fireEvent('trash', this);
26816 download : function(e)
26818 this.fireEvent('download', this);
26821 loadCanvas : function(src)
26823 if(this.fireEvent('beforeloadcanvas', this, src) != false){
26827 this.imageEl = document.createElement('img');
26831 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
26833 this.imageEl.src = src;
26837 onLoadCanvas : function()
26839 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
26840 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
26842 this.bodyEl.un('click', this.beforeSelectFile, this);
26844 this.notifyEl.hide();
26845 this.thumbEl.show();
26846 this.footerEl.show();
26848 this.baseRotateLevel();
26850 if(this.isDocument){
26851 this.setThumbBoxSize();
26854 this.setThumbBoxPosition();
26856 this.baseScaleLevel();
26862 this.canvasLoaded = true;
26865 this.maskEl.unmask();
26870 setCanvasPosition : function()
26872 if(!this.canvasEl){
26876 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
26877 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
26879 this.previewEl.setLeft(pw);
26880 this.previewEl.setTop(ph);
26884 onMouseDown : function(e)
26888 this.dragable = true;
26889 this.pinching = false;
26891 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
26892 this.dragable = false;
26896 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26897 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26901 onMouseMove : function(e)
26905 if(!this.canvasLoaded){
26909 if (!this.dragable){
26913 var minX = Math.ceil(this.thumbEl.getLeft(true));
26914 var minY = Math.ceil(this.thumbEl.getTop(true));
26916 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
26917 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
26919 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26920 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26922 x = x - this.mouseX;
26923 y = y - this.mouseY;
26925 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
26926 var bgY = Math.ceil(y + this.previewEl.getTop(true));
26928 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
26929 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
26931 this.previewEl.setLeft(bgX);
26932 this.previewEl.setTop(bgY);
26934 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26935 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26938 onMouseUp : function(e)
26942 this.dragable = false;
26945 onMouseWheel : function(e)
26949 this.startScale = this.scale;
26951 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
26953 if(!this.zoomable()){
26954 this.scale = this.startScale;
26963 zoomable : function()
26965 var minScale = this.thumbEl.getWidth() / this.minWidth;
26967 if(this.minWidth < this.minHeight){
26968 minScale = this.thumbEl.getHeight() / this.minHeight;
26971 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
26972 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
26976 (this.rotate == 0 || this.rotate == 180) &&
26978 width > this.imageEl.OriginWidth ||
26979 height > this.imageEl.OriginHeight ||
26980 (width < this.minWidth && height < this.minHeight)
26988 (this.rotate == 90 || this.rotate == 270) &&
26990 width > this.imageEl.OriginWidth ||
26991 height > this.imageEl.OriginHeight ||
26992 (width < this.minHeight && height < this.minWidth)
26999 !this.isDocument &&
27000 (this.rotate == 0 || this.rotate == 180) &&
27002 width < this.minWidth ||
27003 width > this.imageEl.OriginWidth ||
27004 height < this.minHeight ||
27005 height > this.imageEl.OriginHeight
27012 !this.isDocument &&
27013 (this.rotate == 90 || this.rotate == 270) &&
27015 width < this.minHeight ||
27016 width > this.imageEl.OriginWidth ||
27017 height < this.minWidth ||
27018 height > this.imageEl.OriginHeight
27028 onRotateLeft : function(e)
27030 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27032 var minScale = this.thumbEl.getWidth() / this.minWidth;
27034 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27035 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27037 this.startScale = this.scale;
27039 while (this.getScaleLevel() < minScale){
27041 this.scale = this.scale + 1;
27043 if(!this.zoomable()){
27048 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27049 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27054 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27061 this.scale = this.startScale;
27063 this.onRotateFail();
27068 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27070 if(this.isDocument){
27071 this.setThumbBoxSize();
27072 this.setThumbBoxPosition();
27073 this.setCanvasPosition();
27078 this.fireEvent('rotate', this, 'left');
27082 onRotateRight : function(e)
27084 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27086 var minScale = this.thumbEl.getWidth() / this.minWidth;
27088 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27089 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27091 this.startScale = this.scale;
27093 while (this.getScaleLevel() < minScale){
27095 this.scale = this.scale + 1;
27097 if(!this.zoomable()){
27102 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27103 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27108 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27115 this.scale = this.startScale;
27117 this.onRotateFail();
27122 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27124 if(this.isDocument){
27125 this.setThumbBoxSize();
27126 this.setThumbBoxPosition();
27127 this.setCanvasPosition();
27132 this.fireEvent('rotate', this, 'right');
27135 onRotateFail : function()
27137 this.errorEl.show(true);
27141 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27146 this.previewEl.dom.innerHTML = '';
27148 var canvasEl = document.createElement("canvas");
27150 var contextEl = canvasEl.getContext("2d");
27152 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27153 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27154 var center = this.imageEl.OriginWidth / 2;
27156 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27157 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27158 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27159 center = this.imageEl.OriginHeight / 2;
27162 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27164 contextEl.translate(center, center);
27165 contextEl.rotate(this.rotate * Math.PI / 180);
27167 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27169 this.canvasEl = document.createElement("canvas");
27171 this.contextEl = this.canvasEl.getContext("2d");
27173 switch (this.rotate) {
27176 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27177 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27179 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27184 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27185 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27187 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27188 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);
27192 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27197 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27198 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27200 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27201 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);
27205 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);
27210 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27211 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27213 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27214 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27218 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);
27225 this.previewEl.appendChild(this.canvasEl);
27227 this.setCanvasPosition();
27232 if(!this.canvasLoaded){
27236 var imageCanvas = document.createElement("canvas");
27238 var imageContext = imageCanvas.getContext("2d");
27240 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27241 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27243 var center = imageCanvas.width / 2;
27245 imageContext.translate(center, center);
27247 imageContext.rotate(this.rotate * Math.PI / 180);
27249 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27251 var canvas = document.createElement("canvas");
27253 var context = canvas.getContext("2d");
27255 canvas.width = this.minWidth;
27256 canvas.height = this.minHeight;
27258 switch (this.rotate) {
27261 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27262 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27264 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27265 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27267 var targetWidth = this.minWidth - 2 * x;
27268 var targetHeight = this.minHeight - 2 * y;
27272 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27273 scale = targetWidth / width;
27276 if(x > 0 && y == 0){
27277 scale = targetHeight / height;
27280 if(x > 0 && y > 0){
27281 scale = targetWidth / width;
27283 if(width < height){
27284 scale = targetHeight / height;
27288 context.scale(scale, scale);
27290 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27291 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27293 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27294 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27296 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27301 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27302 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27304 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27305 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27307 var targetWidth = this.minWidth - 2 * x;
27308 var targetHeight = this.minHeight - 2 * y;
27312 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27313 scale = targetWidth / width;
27316 if(x > 0 && y == 0){
27317 scale = targetHeight / height;
27320 if(x > 0 && y > 0){
27321 scale = targetWidth / width;
27323 if(width < height){
27324 scale = targetHeight / height;
27328 context.scale(scale, scale);
27330 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27331 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27333 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27334 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27336 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27338 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27343 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27344 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27346 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27347 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27349 var targetWidth = this.minWidth - 2 * x;
27350 var targetHeight = this.minHeight - 2 * y;
27354 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27355 scale = targetWidth / width;
27358 if(x > 0 && y == 0){
27359 scale = targetHeight / height;
27362 if(x > 0 && y > 0){
27363 scale = targetWidth / width;
27365 if(width < height){
27366 scale = targetHeight / height;
27370 context.scale(scale, scale);
27372 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27373 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27375 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27376 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27378 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27379 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27381 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27386 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27387 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27389 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27390 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27392 var targetWidth = this.minWidth - 2 * x;
27393 var targetHeight = this.minHeight - 2 * y;
27397 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27398 scale = targetWidth / width;
27401 if(x > 0 && y == 0){
27402 scale = targetHeight / height;
27405 if(x > 0 && y > 0){
27406 scale = targetWidth / width;
27408 if(width < height){
27409 scale = targetHeight / height;
27413 context.scale(scale, scale);
27415 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27416 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27418 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27419 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27421 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27423 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27430 this.cropData = canvas.toDataURL(this.cropType);
27432 if(this.fireEvent('crop', this, this.cropData) !== false){
27433 this.process(this.file, this.cropData);
27440 setThumbBoxSize : function()
27444 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27445 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27446 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27448 this.minWidth = width;
27449 this.minHeight = height;
27451 if(this.rotate == 90 || this.rotate == 270){
27452 this.minWidth = height;
27453 this.minHeight = width;
27458 width = Math.ceil(this.minWidth * height / this.minHeight);
27460 if(this.minWidth > this.minHeight){
27462 height = Math.ceil(this.minHeight * width / this.minWidth);
27465 this.thumbEl.setStyle({
27466 width : width + 'px',
27467 height : height + 'px'
27474 setThumbBoxPosition : function()
27476 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27477 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27479 this.thumbEl.setLeft(x);
27480 this.thumbEl.setTop(y);
27484 baseRotateLevel : function()
27486 this.baseRotate = 1;
27489 typeof(this.exif) != 'undefined' &&
27490 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27491 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27493 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27496 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27500 baseScaleLevel : function()
27504 if(this.isDocument){
27506 if(this.baseRotate == 6 || this.baseRotate == 8){
27508 height = this.thumbEl.getHeight();
27509 this.baseScale = height / this.imageEl.OriginWidth;
27511 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27512 width = this.thumbEl.getWidth();
27513 this.baseScale = width / this.imageEl.OriginHeight;
27519 height = this.thumbEl.getHeight();
27520 this.baseScale = height / this.imageEl.OriginHeight;
27522 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27523 width = this.thumbEl.getWidth();
27524 this.baseScale = width / this.imageEl.OriginWidth;
27530 if(this.baseRotate == 6 || this.baseRotate == 8){
27532 width = this.thumbEl.getHeight();
27533 this.baseScale = width / this.imageEl.OriginHeight;
27535 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27536 height = this.thumbEl.getWidth();
27537 this.baseScale = height / this.imageEl.OriginHeight;
27540 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27541 height = this.thumbEl.getWidth();
27542 this.baseScale = height / this.imageEl.OriginHeight;
27544 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27545 width = this.thumbEl.getHeight();
27546 this.baseScale = width / this.imageEl.OriginWidth;
27553 width = this.thumbEl.getWidth();
27554 this.baseScale = width / this.imageEl.OriginWidth;
27556 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27557 height = this.thumbEl.getHeight();
27558 this.baseScale = height / this.imageEl.OriginHeight;
27561 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27563 height = this.thumbEl.getHeight();
27564 this.baseScale = height / this.imageEl.OriginHeight;
27566 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27567 width = this.thumbEl.getWidth();
27568 this.baseScale = width / this.imageEl.OriginWidth;
27576 getScaleLevel : function()
27578 return this.baseScale * Math.pow(1.1, this.scale);
27581 onTouchStart : function(e)
27583 if(!this.canvasLoaded){
27584 this.beforeSelectFile(e);
27588 var touches = e.browserEvent.touches;
27594 if(touches.length == 1){
27595 this.onMouseDown(e);
27599 if(touches.length != 2){
27605 for(var i = 0, finger; finger = touches[i]; i++){
27606 coords.push(finger.pageX, finger.pageY);
27609 var x = Math.pow(coords[0] - coords[2], 2);
27610 var y = Math.pow(coords[1] - coords[3], 2);
27612 this.startDistance = Math.sqrt(x + y);
27614 this.startScale = this.scale;
27616 this.pinching = true;
27617 this.dragable = false;
27621 onTouchMove : function(e)
27623 if(!this.pinching && !this.dragable){
27627 var touches = e.browserEvent.touches;
27634 this.onMouseMove(e);
27640 for(var i = 0, finger; finger = touches[i]; i++){
27641 coords.push(finger.pageX, finger.pageY);
27644 var x = Math.pow(coords[0] - coords[2], 2);
27645 var y = Math.pow(coords[1] - coords[3], 2);
27647 this.endDistance = Math.sqrt(x + y);
27649 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
27651 if(!this.zoomable()){
27652 this.scale = this.startScale;
27660 onTouchEnd : function(e)
27662 this.pinching = false;
27663 this.dragable = false;
27667 process : function(file, crop)
27670 this.maskEl.mask(this.loadingText);
27673 this.xhr = new XMLHttpRequest();
27675 file.xhr = this.xhr;
27677 this.xhr.open(this.method, this.url, true);
27680 "Accept": "application/json",
27681 "Cache-Control": "no-cache",
27682 "X-Requested-With": "XMLHttpRequest"
27685 for (var headerName in headers) {
27686 var headerValue = headers[headerName];
27688 this.xhr.setRequestHeader(headerName, headerValue);
27694 this.xhr.onload = function()
27696 _this.xhrOnLoad(_this.xhr);
27699 this.xhr.onerror = function()
27701 _this.xhrOnError(_this.xhr);
27704 var formData = new FormData();
27706 formData.append('returnHTML', 'NO');
27709 formData.append('crop', crop);
27712 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
27713 formData.append(this.paramName, file, file.name);
27716 if(typeof(file.filename) != 'undefined'){
27717 formData.append('filename', file.filename);
27720 if(typeof(file.mimetype) != 'undefined'){
27721 formData.append('mimetype', file.mimetype);
27724 if(this.fireEvent('arrange', this, formData) != false){
27725 this.xhr.send(formData);
27729 xhrOnLoad : function(xhr)
27732 this.maskEl.unmask();
27735 if (xhr.readyState !== 4) {
27736 this.fireEvent('exception', this, xhr);
27740 var response = Roo.decode(xhr.responseText);
27742 if(!response.success){
27743 this.fireEvent('exception', this, xhr);
27747 var response = Roo.decode(xhr.responseText);
27749 this.fireEvent('upload', this, response);
27753 xhrOnError : function()
27756 this.maskEl.unmask();
27759 Roo.log('xhr on error');
27761 var response = Roo.decode(xhr.responseText);
27767 prepare : function(file)
27770 this.maskEl.mask(this.loadingText);
27776 if(typeof(file) === 'string'){
27777 this.loadCanvas(file);
27781 if(!file || !this.urlAPI){
27786 this.cropType = file.type;
27790 if(this.fireEvent('prepare', this, this.file) != false){
27792 var reader = new FileReader();
27794 reader.onload = function (e) {
27795 if (e.target.error) {
27796 Roo.log(e.target.error);
27800 var buffer = e.target.result,
27801 dataView = new DataView(buffer),
27803 maxOffset = dataView.byteLength - 4,
27807 if (dataView.getUint16(0) === 0xffd8) {
27808 while (offset < maxOffset) {
27809 markerBytes = dataView.getUint16(offset);
27811 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
27812 markerLength = dataView.getUint16(offset + 2) + 2;
27813 if (offset + markerLength > dataView.byteLength) {
27814 Roo.log('Invalid meta data: Invalid segment size.');
27818 if(markerBytes == 0xffe1){
27819 _this.parseExifData(
27826 offset += markerLength;
27836 var url = _this.urlAPI.createObjectURL(_this.file);
27838 _this.loadCanvas(url);
27843 reader.readAsArrayBuffer(this.file);
27849 parseExifData : function(dataView, offset, length)
27851 var tiffOffset = offset + 10,
27855 if (dataView.getUint32(offset + 4) !== 0x45786966) {
27856 // No Exif data, might be XMP data instead
27860 // Check for the ASCII code for "Exif" (0x45786966):
27861 if (dataView.getUint32(offset + 4) !== 0x45786966) {
27862 // No Exif data, might be XMP data instead
27865 if (tiffOffset + 8 > dataView.byteLength) {
27866 Roo.log('Invalid Exif data: Invalid segment size.');
27869 // Check for the two null bytes:
27870 if (dataView.getUint16(offset + 8) !== 0x0000) {
27871 Roo.log('Invalid Exif data: Missing byte alignment offset.');
27874 // Check the byte alignment:
27875 switch (dataView.getUint16(tiffOffset)) {
27877 littleEndian = true;
27880 littleEndian = false;
27883 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
27886 // Check for the TIFF tag marker (0x002A):
27887 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
27888 Roo.log('Invalid Exif data: Missing TIFF marker.');
27891 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
27892 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
27894 this.parseExifTags(
27897 tiffOffset + dirOffset,
27902 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
27907 if (dirOffset + 6 > dataView.byteLength) {
27908 Roo.log('Invalid Exif data: Invalid directory offset.');
27911 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
27912 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
27913 if (dirEndOffset + 4 > dataView.byteLength) {
27914 Roo.log('Invalid Exif data: Invalid directory size.');
27917 for (i = 0; i < tagsNumber; i += 1) {
27921 dirOffset + 2 + 12 * i, // tag offset
27925 // Return the offset to the next directory:
27926 return dataView.getUint32(dirEndOffset, littleEndian);
27929 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
27931 var tag = dataView.getUint16(offset, littleEndian);
27933 this.exif[tag] = this.getExifValue(
27937 dataView.getUint16(offset + 2, littleEndian), // tag type
27938 dataView.getUint32(offset + 4, littleEndian), // tag length
27943 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
27945 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
27954 Roo.log('Invalid Exif data: Invalid tag type.');
27958 tagSize = tagType.size * length;
27959 // Determine if the value is contained in the dataOffset bytes,
27960 // or if the value at the dataOffset is a pointer to the actual data:
27961 dataOffset = tagSize > 4 ?
27962 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
27963 if (dataOffset + tagSize > dataView.byteLength) {
27964 Roo.log('Invalid Exif data: Invalid data offset.');
27967 if (length === 1) {
27968 return tagType.getValue(dataView, dataOffset, littleEndian);
27971 for (i = 0; i < length; i += 1) {
27972 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
27975 if (tagType.ascii) {
27977 // Concatenate the chars:
27978 for (i = 0; i < values.length; i += 1) {
27980 // Ignore the terminating NULL byte(s):
27981 if (c === '\u0000') {
27993 Roo.apply(Roo.bootstrap.UploadCropbox, {
27995 'Orientation': 0x0112
27999 1: 0, //'top-left',
28001 3: 180, //'bottom-right',
28002 // 4: 'bottom-left',
28004 6: 90, //'right-top',
28005 // 7: 'right-bottom',
28006 8: 270 //'left-bottom'
28010 // byte, 8-bit unsigned int:
28012 getValue: function (dataView, dataOffset) {
28013 return dataView.getUint8(dataOffset);
28017 // ascii, 8-bit byte:
28019 getValue: function (dataView, dataOffset) {
28020 return String.fromCharCode(dataView.getUint8(dataOffset));
28025 // short, 16 bit int:
28027 getValue: function (dataView, dataOffset, littleEndian) {
28028 return dataView.getUint16(dataOffset, littleEndian);
28032 // long, 32 bit int:
28034 getValue: function (dataView, dataOffset, littleEndian) {
28035 return dataView.getUint32(dataOffset, littleEndian);
28039 // rational = two long values, first is numerator, second is denominator:
28041 getValue: function (dataView, dataOffset, littleEndian) {
28042 return dataView.getUint32(dataOffset, littleEndian) /
28043 dataView.getUint32(dataOffset + 4, littleEndian);
28047 // slong, 32 bit signed int:
28049 getValue: function (dataView, dataOffset, littleEndian) {
28050 return dataView.getInt32(dataOffset, littleEndian);
28054 // srational, two slongs, first is numerator, second is denominator:
28056 getValue: function (dataView, dataOffset, littleEndian) {
28057 return dataView.getInt32(dataOffset, littleEndian) /
28058 dataView.getInt32(dataOffset + 4, littleEndian);
28068 cls : 'btn-group roo-upload-cropbox-rotate-left',
28069 action : 'rotate-left',
28073 cls : 'btn btn-default',
28074 html : '<i class="fa fa-undo"></i>'
28080 cls : 'btn-group roo-upload-cropbox-picture',
28081 action : 'picture',
28085 cls : 'btn btn-default',
28086 html : '<i class="fa fa-picture-o"></i>'
28092 cls : 'btn-group roo-upload-cropbox-rotate-right',
28093 action : 'rotate-right',
28097 cls : 'btn btn-default',
28098 html : '<i class="fa fa-repeat"></i>'
28106 cls : 'btn-group roo-upload-cropbox-rotate-left',
28107 action : 'rotate-left',
28111 cls : 'btn btn-default',
28112 html : '<i class="fa fa-undo"></i>'
28118 cls : 'btn-group roo-upload-cropbox-download',
28119 action : 'download',
28123 cls : 'btn btn-default',
28124 html : '<i class="fa fa-download"></i>'
28130 cls : 'btn-group roo-upload-cropbox-crop',
28135 cls : 'btn btn-default',
28136 html : '<i class="fa fa-crop"></i>'
28142 cls : 'btn-group roo-upload-cropbox-trash',
28147 cls : 'btn btn-default',
28148 html : '<i class="fa fa-trash"></i>'
28154 cls : 'btn-group roo-upload-cropbox-rotate-right',
28155 action : 'rotate-right',
28159 cls : 'btn btn-default',
28160 html : '<i class="fa fa-repeat"></i>'
28168 cls : 'btn-group roo-upload-cropbox-rotate-left',
28169 action : 'rotate-left',
28173 cls : 'btn btn-default',
28174 html : '<i class="fa fa-undo"></i>'
28180 cls : 'btn-group roo-upload-cropbox-rotate-right',
28181 action : 'rotate-right',
28185 cls : 'btn btn-default',
28186 html : '<i class="fa fa-repeat"></i>'
28199 * @class Roo.bootstrap.DocumentManager
28200 * @extends Roo.bootstrap.Component
28201 * Bootstrap DocumentManager class
28202 * @cfg {String} paramName default 'imageUpload'
28203 * @cfg {String} toolTipName default 'filename'
28204 * @cfg {String} method default POST
28205 * @cfg {String} url action url
28206 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28207 * @cfg {Boolean} multiple multiple upload default true
28208 * @cfg {Number} thumbSize default 300
28209 * @cfg {String} fieldLabel
28210 * @cfg {Number} labelWidth default 4
28211 * @cfg {String} labelAlign (left|top) default left
28212 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28213 * @cfg {Number} labellg set the width of label (1-12)
28214 * @cfg {Number} labelmd set the width of label (1-12)
28215 * @cfg {Number} labelsm set the width of label (1-12)
28216 * @cfg {Number} labelxs set the width of label (1-12)
28219 * Create a new DocumentManager
28220 * @param {Object} config The config object
28223 Roo.bootstrap.DocumentManager = function(config){
28224 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28227 this.delegates = [];
28232 * Fire when initial the DocumentManager
28233 * @param {Roo.bootstrap.DocumentManager} this
28238 * inspect selected file
28239 * @param {Roo.bootstrap.DocumentManager} this
28240 * @param {File} file
28245 * Fire when xhr load exception
28246 * @param {Roo.bootstrap.DocumentManager} this
28247 * @param {XMLHttpRequest} xhr
28249 "exception" : true,
28251 * @event afterupload
28252 * Fire when xhr load exception
28253 * @param {Roo.bootstrap.DocumentManager} this
28254 * @param {XMLHttpRequest} xhr
28256 "afterupload" : true,
28259 * prepare the form data
28260 * @param {Roo.bootstrap.DocumentManager} this
28261 * @param {Object} formData
28266 * Fire when remove the file
28267 * @param {Roo.bootstrap.DocumentManager} this
28268 * @param {Object} file
28273 * Fire after refresh the file
28274 * @param {Roo.bootstrap.DocumentManager} this
28279 * Fire after click the image
28280 * @param {Roo.bootstrap.DocumentManager} this
28281 * @param {Object} file
28286 * Fire when upload a image and editable set to true
28287 * @param {Roo.bootstrap.DocumentManager} this
28288 * @param {Object} file
28292 * @event beforeselectfile
28293 * Fire before select file
28294 * @param {Roo.bootstrap.DocumentManager} this
28296 "beforeselectfile" : true,
28299 * Fire before process file
28300 * @param {Roo.bootstrap.DocumentManager} this
28301 * @param {Object} file
28308 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
28317 paramName : 'imageUpload',
28318 toolTipName : 'filename',
28321 labelAlign : 'left',
28331 getAutoCreate : function()
28333 var managerWidget = {
28335 cls : 'roo-document-manager',
28339 cls : 'roo-document-manager-selector',
28344 cls : 'roo-document-manager-uploader',
28348 cls : 'roo-document-manager-upload-btn',
28349 html : '<i class="fa fa-plus"></i>'
28360 cls : 'column col-md-12',
28365 if(this.fieldLabel.length){
28370 cls : 'column col-md-12',
28371 html : this.fieldLabel
28375 cls : 'column col-md-12',
28380 if(this.labelAlign == 'left'){
28385 html : this.fieldLabel
28394 if(this.labelWidth > 12){
28395 content[0].style = "width: " + this.labelWidth + 'px';
28398 if(this.labelWidth < 13 && this.labelmd == 0){
28399 this.labelmd = this.labelWidth;
28402 if(this.labellg > 0){
28403 content[0].cls += ' col-lg-' + this.labellg;
28404 content[1].cls += ' col-lg-' + (12 - this.labellg);
28407 if(this.labelmd > 0){
28408 content[0].cls += ' col-md-' + this.labelmd;
28409 content[1].cls += ' col-md-' + (12 - this.labelmd);
28412 if(this.labelsm > 0){
28413 content[0].cls += ' col-sm-' + this.labelsm;
28414 content[1].cls += ' col-sm-' + (12 - this.labelsm);
28417 if(this.labelxs > 0){
28418 content[0].cls += ' col-xs-' + this.labelxs;
28419 content[1].cls += ' col-xs-' + (12 - this.labelxs);
28427 cls : 'row clearfix',
28435 initEvents : function()
28437 this.managerEl = this.el.select('.roo-document-manager', true).first();
28438 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28440 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28441 this.selectorEl.hide();
28444 this.selectorEl.attr('multiple', 'multiple');
28447 this.selectorEl.on('change', this.onFileSelected, this);
28449 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28450 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28452 this.uploader.on('click', this.onUploaderClick, this);
28454 this.renderProgressDialog();
28458 window.addEventListener("resize", function() { _this.refresh(); } );
28460 this.fireEvent('initial', this);
28463 renderProgressDialog : function()
28467 this.progressDialog = new Roo.bootstrap.Modal({
28468 cls : 'roo-document-manager-progress-dialog',
28469 allow_close : false,
28479 btnclick : function() {
28480 _this.uploadCancel();
28486 this.progressDialog.render(Roo.get(document.body));
28488 this.progress = new Roo.bootstrap.Progress({
28489 cls : 'roo-document-manager-progress',
28494 this.progress.render(this.progressDialog.getChildContainer());
28496 this.progressBar = new Roo.bootstrap.ProgressBar({
28497 cls : 'roo-document-manager-progress-bar',
28500 aria_valuemax : 12,
28504 this.progressBar.render(this.progress.getChildContainer());
28507 onUploaderClick : function(e)
28509 e.preventDefault();
28511 if(this.fireEvent('beforeselectfile', this) != false){
28512 this.selectorEl.dom.click();
28517 onFileSelected : function(e)
28519 e.preventDefault();
28521 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28525 Roo.each(this.selectorEl.dom.files, function(file){
28526 if(this.fireEvent('inspect', this, file) != false){
28527 this.files.push(file);
28537 this.selectorEl.dom.value = '';
28539 if(!this.files.length){
28543 if(this.boxes > 0 && this.files.length > this.boxes){
28544 this.files = this.files.slice(0, this.boxes);
28547 this.uploader.show();
28549 if(this.boxes > 0 && this.files.length > this.boxes - 1){
28550 this.uploader.hide();
28559 Roo.each(this.files, function(file){
28561 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28562 var f = this.renderPreview(file);
28567 if(file.type.indexOf('image') != -1){
28568 this.delegates.push(
28570 _this.process(file);
28571 }).createDelegate(this)
28579 _this.process(file);
28580 }).createDelegate(this)
28585 this.files = files;
28587 this.delegates = this.delegates.concat(docs);
28589 if(!this.delegates.length){
28594 this.progressBar.aria_valuemax = this.delegates.length;
28601 arrange : function()
28603 if(!this.delegates.length){
28604 this.progressDialog.hide();
28609 var delegate = this.delegates.shift();
28611 this.progressDialog.show();
28613 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
28615 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
28620 refresh : function()
28622 this.uploader.show();
28624 if(this.boxes > 0 && this.files.length > this.boxes - 1){
28625 this.uploader.hide();
28628 Roo.isTouch ? this.closable(false) : this.closable(true);
28630 this.fireEvent('refresh', this);
28633 onRemove : function(e, el, o)
28635 e.preventDefault();
28637 this.fireEvent('remove', this, o);
28641 remove : function(o)
28645 Roo.each(this.files, function(file){
28646 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
28655 this.files = files;
28662 Roo.each(this.files, function(file){
28667 file.target.remove();
28676 onClick : function(e, el, o)
28678 e.preventDefault();
28680 this.fireEvent('click', this, o);
28684 closable : function(closable)
28686 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
28688 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28700 xhrOnLoad : function(xhr)
28702 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28706 if (xhr.readyState !== 4) {
28708 this.fireEvent('exception', this, xhr);
28712 var response = Roo.decode(xhr.responseText);
28714 if(!response.success){
28716 this.fireEvent('exception', this, xhr);
28720 var file = this.renderPreview(response.data);
28722 this.files.push(file);
28726 this.fireEvent('afterupload', this, xhr);
28730 xhrOnError : function(xhr)
28732 Roo.log('xhr on error');
28734 var response = Roo.decode(xhr.responseText);
28741 process : function(file)
28743 if(this.fireEvent('process', this, file) !== false){
28744 if(this.editable && file.type.indexOf('image') != -1){
28745 this.fireEvent('edit', this, file);
28749 this.uploadStart(file, false);
28756 uploadStart : function(file, crop)
28758 this.xhr = new XMLHttpRequest();
28760 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28765 file.xhr = this.xhr;
28767 this.managerEl.createChild({
28769 cls : 'roo-document-manager-loading',
28773 tooltip : file.name,
28774 cls : 'roo-document-manager-thumb',
28775 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28781 this.xhr.open(this.method, this.url, true);
28784 "Accept": "application/json",
28785 "Cache-Control": "no-cache",
28786 "X-Requested-With": "XMLHttpRequest"
28789 for (var headerName in headers) {
28790 var headerValue = headers[headerName];
28792 this.xhr.setRequestHeader(headerName, headerValue);
28798 this.xhr.onload = function()
28800 _this.xhrOnLoad(_this.xhr);
28803 this.xhr.onerror = function()
28805 _this.xhrOnError(_this.xhr);
28808 var formData = new FormData();
28810 formData.append('returnHTML', 'NO');
28813 formData.append('crop', crop);
28816 formData.append(this.paramName, file, file.name);
28823 if(this.fireEvent('prepare', this, formData, options) != false){
28825 if(options.manually){
28829 this.xhr.send(formData);
28833 this.uploadCancel();
28836 uploadCancel : function()
28842 this.delegates = [];
28844 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28851 renderPreview : function(file)
28853 if(typeof(file.target) != 'undefined' && file.target){
28857 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
28859 var previewEl = this.managerEl.createChild({
28861 cls : 'roo-document-manager-preview',
28865 tooltip : file[this.toolTipName],
28866 cls : 'roo-document-manager-thumb',
28867 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
28872 html : '<i class="fa fa-times-circle"></i>'
28877 var close = previewEl.select('button.close', true).first();
28879 close.on('click', this.onRemove, this, file);
28881 file.target = previewEl;
28883 var image = previewEl.select('img', true).first();
28887 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
28889 image.on('click', this.onClick, this, file);
28895 onPreviewLoad : function(file, image)
28897 if(typeof(file.target) == 'undefined' || !file.target){
28901 var width = image.dom.naturalWidth || image.dom.width;
28902 var height = image.dom.naturalHeight || image.dom.height;
28904 if(width > height){
28905 file.target.addClass('wide');
28909 file.target.addClass('tall');
28914 uploadFromSource : function(file, crop)
28916 this.xhr = new XMLHttpRequest();
28918 this.managerEl.createChild({
28920 cls : 'roo-document-manager-loading',
28924 tooltip : file.name,
28925 cls : 'roo-document-manager-thumb',
28926 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28932 this.xhr.open(this.method, this.url, true);
28935 "Accept": "application/json",
28936 "Cache-Control": "no-cache",
28937 "X-Requested-With": "XMLHttpRequest"
28940 for (var headerName in headers) {
28941 var headerValue = headers[headerName];
28943 this.xhr.setRequestHeader(headerName, headerValue);
28949 this.xhr.onload = function()
28951 _this.xhrOnLoad(_this.xhr);
28954 this.xhr.onerror = function()
28956 _this.xhrOnError(_this.xhr);
28959 var formData = new FormData();
28961 formData.append('returnHTML', 'NO');
28963 formData.append('crop', crop);
28965 if(typeof(file.filename) != 'undefined'){
28966 formData.append('filename', file.filename);
28969 if(typeof(file.mimetype) != 'undefined'){
28970 formData.append('mimetype', file.mimetype);
28975 if(this.fireEvent('prepare', this, formData) != false){
28976 this.xhr.send(formData);
28986 * @class Roo.bootstrap.DocumentViewer
28987 * @extends Roo.bootstrap.Component
28988 * Bootstrap DocumentViewer class
28989 * @cfg {Boolean} showDownload (true|false) show download button (default true)
28990 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
28993 * Create a new DocumentViewer
28994 * @param {Object} config The config object
28997 Roo.bootstrap.DocumentViewer = function(config){
28998 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29003 * Fire after initEvent
29004 * @param {Roo.bootstrap.DocumentViewer} this
29010 * @param {Roo.bootstrap.DocumentViewer} this
29015 * Fire after download button
29016 * @param {Roo.bootstrap.DocumentViewer} this
29021 * Fire after trash button
29022 * @param {Roo.bootstrap.DocumentViewer} this
29029 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
29031 showDownload : true,
29035 getAutoCreate : function()
29039 cls : 'roo-document-viewer',
29043 cls : 'roo-document-viewer-body',
29047 cls : 'roo-document-viewer-thumb',
29051 cls : 'roo-document-viewer-image'
29059 cls : 'roo-document-viewer-footer',
29062 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29066 cls : 'btn-group roo-document-viewer-download',
29070 cls : 'btn btn-default',
29071 html : '<i class="fa fa-download"></i>'
29077 cls : 'btn-group roo-document-viewer-trash',
29081 cls : 'btn btn-default',
29082 html : '<i class="fa fa-trash"></i>'
29095 initEvents : function()
29097 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29098 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29100 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29101 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29103 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29104 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29106 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29107 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29109 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29110 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29112 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29113 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29115 this.bodyEl.on('click', this.onClick, this);
29116 this.downloadBtn.on('click', this.onDownload, this);
29117 this.trashBtn.on('click', this.onTrash, this);
29119 this.downloadBtn.hide();
29120 this.trashBtn.hide();
29122 if(this.showDownload){
29123 this.downloadBtn.show();
29126 if(this.showTrash){
29127 this.trashBtn.show();
29130 if(!this.showDownload && !this.showTrash) {
29131 this.footerEl.hide();
29136 initial : function()
29138 this.fireEvent('initial', this);
29142 onClick : function(e)
29144 e.preventDefault();
29146 this.fireEvent('click', this);
29149 onDownload : function(e)
29151 e.preventDefault();
29153 this.fireEvent('download', this);
29156 onTrash : function(e)
29158 e.preventDefault();
29160 this.fireEvent('trash', this);
29172 * @class Roo.bootstrap.NavProgressBar
29173 * @extends Roo.bootstrap.Component
29174 * Bootstrap NavProgressBar class
29177 * Create a new nav progress bar
29178 * @param {Object} config The config object
29181 Roo.bootstrap.NavProgressBar = function(config){
29182 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29184 this.bullets = this.bullets || [];
29186 // Roo.bootstrap.NavProgressBar.register(this);
29190 * Fires when the active item changes
29191 * @param {Roo.bootstrap.NavProgressBar} this
29192 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29193 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
29200 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
29205 getAutoCreate : function()
29207 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29211 cls : 'roo-navigation-bar-group',
29215 cls : 'roo-navigation-top-bar'
29219 cls : 'roo-navigation-bullets-bar',
29223 cls : 'roo-navigation-bar'
29230 cls : 'roo-navigation-bottom-bar'
29240 initEvents: function()
29245 onRender : function(ct, position)
29247 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29249 if(this.bullets.length){
29250 Roo.each(this.bullets, function(b){
29259 addItem : function(cfg)
29261 var item = new Roo.bootstrap.NavProgressItem(cfg);
29263 item.parentId = this.id;
29264 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29267 var top = new Roo.bootstrap.Element({
29269 cls : 'roo-navigation-bar-text'
29272 var bottom = new Roo.bootstrap.Element({
29274 cls : 'roo-navigation-bar-text'
29277 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29278 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29280 var topText = new Roo.bootstrap.Element({
29282 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29285 var bottomText = new Roo.bootstrap.Element({
29287 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29290 topText.onRender(top.el, null);
29291 bottomText.onRender(bottom.el, null);
29294 item.bottomEl = bottom;
29297 this.barItems.push(item);
29302 getActive : function()
29304 var active = false;
29306 Roo.each(this.barItems, function(v){
29308 if (!v.isActive()) {
29320 setActiveItem : function(item)
29324 Roo.each(this.barItems, function(v){
29325 if (v.rid == item.rid) {
29329 if (v.isActive()) {
29330 v.setActive(false);
29335 item.setActive(true);
29337 this.fireEvent('changed', this, item, prev);
29340 getBarItem: function(rid)
29344 Roo.each(this.barItems, function(e) {
29345 if (e.rid != rid) {
29356 indexOfItem : function(item)
29360 Roo.each(this.barItems, function(v, i){
29362 if (v.rid != item.rid) {
29373 setActiveNext : function()
29375 var i = this.indexOfItem(this.getActive());
29377 if (i > this.barItems.length) {
29381 this.setActiveItem(this.barItems[i+1]);
29384 setActivePrev : function()
29386 var i = this.indexOfItem(this.getActive());
29392 this.setActiveItem(this.barItems[i-1]);
29395 format : function()
29397 if(!this.barItems.length){
29401 var width = 100 / this.barItems.length;
29403 Roo.each(this.barItems, function(i){
29404 i.el.setStyle('width', width + '%');
29405 i.topEl.el.setStyle('width', width + '%');
29406 i.bottomEl.el.setStyle('width', width + '%');
29415 * Nav Progress Item
29420 * @class Roo.bootstrap.NavProgressItem
29421 * @extends Roo.bootstrap.Component
29422 * Bootstrap NavProgressItem class
29423 * @cfg {String} rid the reference id
29424 * @cfg {Boolean} active (true|false) Is item active default false
29425 * @cfg {Boolean} disabled (true|false) Is item active default false
29426 * @cfg {String} html
29427 * @cfg {String} position (top|bottom) text position default bottom
29428 * @cfg {String} icon show icon instead of number
29431 * Create a new NavProgressItem
29432 * @param {Object} config The config object
29434 Roo.bootstrap.NavProgressItem = function(config){
29435 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29440 * The raw click event for the entire grid.
29441 * @param {Roo.bootstrap.NavProgressItem} this
29442 * @param {Roo.EventObject} e
29449 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
29455 position : 'bottom',
29458 getAutoCreate : function()
29460 var iconCls = 'roo-navigation-bar-item-icon';
29462 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29466 cls: 'roo-navigation-bar-item',
29476 cfg.cls += ' active';
29479 cfg.cls += ' disabled';
29485 disable : function()
29487 this.setDisabled(true);
29490 enable : function()
29492 this.setDisabled(false);
29495 initEvents: function()
29497 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29499 this.iconEl.on('click', this.onClick, this);
29502 onClick : function(e)
29504 e.preventDefault();
29510 if(this.fireEvent('click', this, e) === false){
29514 this.parent().setActiveItem(this);
29517 isActive: function ()
29519 return this.active;
29522 setActive : function(state)
29524 if(this.active == state){
29528 this.active = state;
29531 this.el.addClass('active');
29535 this.el.removeClass('active');
29540 setDisabled : function(state)
29542 if(this.disabled == state){
29546 this.disabled = state;
29549 this.el.addClass('disabled');
29553 this.el.removeClass('disabled');
29556 tooltipEl : function()
29558 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29571 * @class Roo.bootstrap.FieldLabel
29572 * @extends Roo.bootstrap.Component
29573 * Bootstrap FieldLabel class
29574 * @cfg {String} html contents of the element
29575 * @cfg {String} tag tag of the element default label
29576 * @cfg {String} cls class of the element
29577 * @cfg {String} target label target
29578 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
29579 * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
29580 * @cfg {String} validClass default "text-success fa fa-lg fa-check"
29581 * @cfg {String} iconTooltip default "This field is required"
29584 * Create a new FieldLabel
29585 * @param {Object} config The config object
29588 Roo.bootstrap.FieldLabel = function(config){
29589 Roo.bootstrap.Element.superclass.constructor.call(this, config);
29594 * Fires after the field has been marked as invalid.
29595 * @param {Roo.form.FieldLabel} this
29596 * @param {String} msg The validation message
29601 * Fires after the field has been validated with no errors.
29602 * @param {Roo.form.FieldLabel} this
29608 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
29615 invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
29616 validClass : 'text-success fa fa-lg fa-check',
29617 iconTooltip : 'This field is required',
29619 getAutoCreate : function(){
29623 cls : 'roo-bootstrap-field-label ' + this.cls,
29629 tooltip : this.iconTooltip
29641 initEvents: function()
29643 Roo.bootstrap.Element.superclass.initEvents.call(this);
29645 this.iconEl = this.el.select('i', true).first();
29647 this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
29649 Roo.bootstrap.FieldLabel.register(this);
29653 * Mark this field as valid
29655 markValid : function()
29657 this.iconEl.show();
29659 this.iconEl.removeClass(this.invalidClass);
29661 this.iconEl.addClass(this.validClass);
29663 this.fireEvent('valid', this);
29667 * Mark this field as invalid
29668 * @param {String} msg The validation message
29670 markInvalid : function(msg)
29672 this.iconEl.show();
29674 this.iconEl.removeClass(this.validClass);
29676 this.iconEl.addClass(this.invalidClass);
29678 this.fireEvent('invalid', this, msg);
29684 Roo.apply(Roo.bootstrap.FieldLabel, {
29689 * register a FieldLabel Group
29690 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
29692 register : function(label)
29694 if(this.groups.hasOwnProperty(label.target)){
29698 this.groups[label.target] = label;
29702 * fetch a FieldLabel Group based on the target
29703 * @param {string} target
29704 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
29706 get: function(target) {
29707 if (typeof(this.groups[target]) == 'undefined') {
29711 return this.groups[target] ;
29720 * page DateSplitField.
29726 * @class Roo.bootstrap.DateSplitField
29727 * @extends Roo.bootstrap.Component
29728 * Bootstrap DateSplitField class
29729 * @cfg {string} fieldLabel - the label associated
29730 * @cfg {Number} labelWidth set the width of label (0-12)
29731 * @cfg {String} labelAlign (top|left)
29732 * @cfg {Boolean} dayAllowBlank (true|false) default false
29733 * @cfg {Boolean} monthAllowBlank (true|false) default false
29734 * @cfg {Boolean} yearAllowBlank (true|false) default false
29735 * @cfg {string} dayPlaceholder
29736 * @cfg {string} monthPlaceholder
29737 * @cfg {string} yearPlaceholder
29738 * @cfg {string} dayFormat default 'd'
29739 * @cfg {string} monthFormat default 'm'
29740 * @cfg {string} yearFormat default 'Y'
29741 * @cfg {Number} labellg set the width of label (1-12)
29742 * @cfg {Number} labelmd set the width of label (1-12)
29743 * @cfg {Number} labelsm set the width of label (1-12)
29744 * @cfg {Number} labelxs set the width of label (1-12)
29748 * Create a new DateSplitField
29749 * @param {Object} config The config object
29752 Roo.bootstrap.DateSplitField = function(config){
29753 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
29759 * getting the data of years
29760 * @param {Roo.bootstrap.DateSplitField} this
29761 * @param {Object} years
29766 * getting the data of days
29767 * @param {Roo.bootstrap.DateSplitField} this
29768 * @param {Object} days
29773 * Fires after the field has been marked as invalid.
29774 * @param {Roo.form.Field} this
29775 * @param {String} msg The validation message
29780 * Fires after the field has been validated with no errors.
29781 * @param {Roo.form.Field} this
29787 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
29790 labelAlign : 'top',
29792 dayAllowBlank : false,
29793 monthAllowBlank : false,
29794 yearAllowBlank : false,
29795 dayPlaceholder : '',
29796 monthPlaceholder : '',
29797 yearPlaceholder : '',
29801 isFormField : true,
29807 getAutoCreate : function()
29811 cls : 'row roo-date-split-field-group',
29816 cls : 'form-hidden-field roo-date-split-field-group-value',
29822 var labelCls = 'col-md-12';
29823 var contentCls = 'col-md-4';
29825 if(this.fieldLabel){
29829 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
29833 html : this.fieldLabel
29838 if(this.labelAlign == 'left'){
29840 if(this.labelWidth > 12){
29841 label.style = "width: " + this.labelWidth + 'px';
29844 if(this.labelWidth < 13 && this.labelmd == 0){
29845 this.labelmd = this.labelWidth;
29848 if(this.labellg > 0){
29849 labelCls = ' col-lg-' + this.labellg;
29850 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
29853 if(this.labelmd > 0){
29854 labelCls = ' col-md-' + this.labelmd;
29855 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
29858 if(this.labelsm > 0){
29859 labelCls = ' col-sm-' + this.labelsm;
29860 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
29863 if(this.labelxs > 0){
29864 labelCls = ' col-xs-' + this.labelxs;
29865 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
29869 label.cls += ' ' + labelCls;
29871 cfg.cn.push(label);
29874 Roo.each(['day', 'month', 'year'], function(t){
29877 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
29884 inputEl: function ()
29886 return this.el.select('.roo-date-split-field-group-value', true).first();
29889 onRender : function(ct, position)
29893 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29895 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
29897 this.dayField = new Roo.bootstrap.ComboBox({
29898 allowBlank : this.dayAllowBlank,
29899 alwaysQuery : true,
29900 displayField : 'value',
29903 forceSelection : true,
29905 placeholder : this.dayPlaceholder,
29906 selectOnFocus : true,
29907 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29908 triggerAction : 'all',
29910 valueField : 'value',
29911 store : new Roo.data.SimpleStore({
29912 data : (function() {
29914 _this.fireEvent('days', _this, days);
29917 fields : [ 'value' ]
29920 select : function (_self, record, index)
29922 _this.setValue(_this.getValue());
29927 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
29929 this.monthField = new Roo.bootstrap.MonthField({
29930 after : '<i class=\"fa fa-calendar\"></i>',
29931 allowBlank : this.monthAllowBlank,
29932 placeholder : this.monthPlaceholder,
29935 render : function (_self)
29937 this.el.select('span.input-group-addon', true).first().on('click', function(e){
29938 e.preventDefault();
29942 select : function (_self, oldvalue, newvalue)
29944 _this.setValue(_this.getValue());
29949 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
29951 this.yearField = new Roo.bootstrap.ComboBox({
29952 allowBlank : this.yearAllowBlank,
29953 alwaysQuery : true,
29954 displayField : 'value',
29957 forceSelection : true,
29959 placeholder : this.yearPlaceholder,
29960 selectOnFocus : true,
29961 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29962 triggerAction : 'all',
29964 valueField : 'value',
29965 store : new Roo.data.SimpleStore({
29966 data : (function() {
29968 _this.fireEvent('years', _this, years);
29971 fields : [ 'value' ]
29974 select : function (_self, record, index)
29976 _this.setValue(_this.getValue());
29981 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
29984 setValue : function(v, format)
29986 this.inputEl.dom.value = v;
29988 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
29990 var d = Date.parseDate(v, f);
29997 this.setDay(d.format(this.dayFormat));
29998 this.setMonth(d.format(this.monthFormat));
29999 this.setYear(d.format(this.yearFormat));
30006 setDay : function(v)
30008 this.dayField.setValue(v);
30009 this.inputEl.dom.value = this.getValue();
30014 setMonth : function(v)
30016 this.monthField.setValue(v, true);
30017 this.inputEl.dom.value = this.getValue();
30022 setYear : function(v)
30024 this.yearField.setValue(v);
30025 this.inputEl.dom.value = this.getValue();
30030 getDay : function()
30032 return this.dayField.getValue();
30035 getMonth : function()
30037 return this.monthField.getValue();
30040 getYear : function()
30042 return this.yearField.getValue();
30045 getValue : function()
30047 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30049 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30059 this.inputEl.dom.value = '';
30064 validate : function()
30066 var d = this.dayField.validate();
30067 var m = this.monthField.validate();
30068 var y = this.yearField.validate();
30073 (!this.dayAllowBlank && !d) ||
30074 (!this.monthAllowBlank && !m) ||
30075 (!this.yearAllowBlank && !y)
30080 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30089 this.markInvalid();
30094 markValid : function()
30097 var label = this.el.select('label', true).first();
30098 var icon = this.el.select('i.fa-star', true).first();
30104 this.fireEvent('valid', this);
30108 * Mark this field as invalid
30109 * @param {String} msg The validation message
30111 markInvalid : function(msg)
30114 var label = this.el.select('label', true).first();
30115 var icon = this.el.select('i.fa-star', true).first();
30117 if(label && !icon){
30118 this.el.select('.roo-date-split-field-label', true).createChild({
30120 cls : 'text-danger fa fa-lg fa-star',
30121 tooltip : 'This field is required',
30122 style : 'margin-right:5px;'
30126 this.fireEvent('invalid', this, msg);
30129 clearInvalid : function()
30131 var label = this.el.select('label', true).first();
30132 var icon = this.el.select('i.fa-star', true).first();
30138 this.fireEvent('valid', this);
30141 getName: function()
30151 * http://masonry.desandro.com
30153 * The idea is to render all the bricks based on vertical width...
30155 * The original code extends 'outlayer' - we might need to use that....
30161 * @class Roo.bootstrap.LayoutMasonry
30162 * @extends Roo.bootstrap.Component
30163 * Bootstrap Layout Masonry class
30166 * Create a new Element
30167 * @param {Object} config The config object
30170 Roo.bootstrap.LayoutMasonry = function(config){
30172 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30176 Roo.bootstrap.LayoutMasonry.register(this);
30182 * Fire after layout the items
30183 * @param {Roo.bootstrap.LayoutMasonry} this
30184 * @param {Roo.EventObject} e
30191 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
30194 * @cfg {Boolean} isLayoutInstant = no animation?
30196 isLayoutInstant : false, // needed?
30199 * @cfg {Number} boxWidth width of the columns
30204 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
30209 * @cfg {Number} padWidth padding below box..
30214 * @cfg {Number} gutter gutter width..
30219 * @cfg {Number} maxCols maximum number of columns
30225 * @cfg {Boolean} isAutoInitial defalut true
30227 isAutoInitial : true,
30232 * @cfg {Boolean} isHorizontal defalut false
30234 isHorizontal : false,
30236 currentSize : null,
30242 bricks: null, //CompositeElement
30246 _isLayoutInited : false,
30248 // isAlternative : false, // only use for vertical layout...
30251 * @cfg {Number} alternativePadWidth padding below box..
30253 alternativePadWidth : 50,
30255 selectedBrick : [],
30257 getAutoCreate : function(){
30259 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30263 cls: 'blog-masonary-wrapper ' + this.cls,
30265 cls : 'mas-boxes masonary'
30272 getChildContainer: function( )
30274 if (this.boxesEl) {
30275 return this.boxesEl;
30278 this.boxesEl = this.el.select('.mas-boxes').first();
30280 return this.boxesEl;
30284 initEvents : function()
30288 if(this.isAutoInitial){
30289 Roo.log('hook children rendered');
30290 this.on('childrenrendered', function() {
30291 Roo.log('children rendered');
30297 initial : function()
30299 this.selectedBrick = [];
30301 this.currentSize = this.el.getBox(true);
30303 Roo.EventManager.onWindowResize(this.resize, this);
30305 if(!this.isAutoInitial){
30313 //this.layout.defer(500,this);
30317 resize : function()
30319 var cs = this.el.getBox(true);
30322 this.currentSize.width == cs.width &&
30323 this.currentSize.x == cs.x &&
30324 this.currentSize.height == cs.height &&
30325 this.currentSize.y == cs.y
30327 Roo.log("no change in with or X or Y");
30331 this.currentSize = cs;
30337 layout : function()
30339 this._resetLayout();
30341 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30343 this.layoutItems( isInstant );
30345 this._isLayoutInited = true;
30347 this.fireEvent('layout', this);
30351 _resetLayout : function()
30353 if(this.isHorizontal){
30354 this.horizontalMeasureColumns();
30358 this.verticalMeasureColumns();
30362 verticalMeasureColumns : function()
30364 this.getContainerWidth();
30366 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30367 // this.colWidth = Math.floor(this.containerWidth * 0.8);
30371 var boxWidth = this.boxWidth + this.padWidth;
30373 if(this.containerWidth < this.boxWidth){
30374 boxWidth = this.containerWidth
30377 var containerWidth = this.containerWidth;
30379 var cols = Math.floor(containerWidth / boxWidth);
30381 this.cols = Math.max( cols, 1 );
30383 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30385 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30387 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30389 this.colWidth = boxWidth + avail - this.padWidth;
30391 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30392 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
30395 horizontalMeasureColumns : function()
30397 this.getContainerWidth();
30399 var boxWidth = this.boxWidth;
30401 if(this.containerWidth < boxWidth){
30402 boxWidth = this.containerWidth;
30405 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30407 this.el.setHeight(boxWidth);
30411 getContainerWidth : function()
30413 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
30416 layoutItems : function( isInstant )
30418 Roo.log(this.bricks);
30420 var items = Roo.apply([], this.bricks);
30422 if(this.isHorizontal){
30423 this._horizontalLayoutItems( items , isInstant );
30427 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30428 // this._verticalAlternativeLayoutItems( items , isInstant );
30432 this._verticalLayoutItems( items , isInstant );
30436 _verticalLayoutItems : function ( items , isInstant)
30438 if ( !items || !items.length ) {
30443 ['xs', 'xs', 'xs', 'tall'],
30444 ['xs', 'xs', 'tall'],
30445 ['xs', 'xs', 'sm'],
30446 ['xs', 'xs', 'xs'],
30452 ['sm', 'xs', 'xs'],
30456 ['tall', 'xs', 'xs', 'xs'],
30457 ['tall', 'xs', 'xs'],
30469 Roo.each(items, function(item, k){
30471 switch (item.size) {
30472 // these layouts take up a full box,
30483 boxes.push([item]);
30506 var filterPattern = function(box, length)
30514 var pattern = box.slice(0, length);
30518 Roo.each(pattern, function(i){
30519 format.push(i.size);
30522 Roo.each(standard, function(s){
30524 if(String(s) != String(format)){
30533 if(!match && length == 1){
30538 filterPattern(box, length - 1);
30542 queue.push(pattern);
30544 box = box.slice(length, box.length);
30546 filterPattern(box, 4);
30552 Roo.each(boxes, function(box, k){
30558 if(box.length == 1){
30563 filterPattern(box, 4);
30567 this._processVerticalLayoutQueue( queue, isInstant );
30571 // _verticalAlternativeLayoutItems : function( items , isInstant )
30573 // if ( !items || !items.length ) {
30577 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
30581 _horizontalLayoutItems : function ( items , isInstant)
30583 if ( !items || !items.length || items.length < 3) {
30589 var eItems = items.slice(0, 3);
30591 items = items.slice(3, items.length);
30594 ['xs', 'xs', 'xs', 'wide'],
30595 ['xs', 'xs', 'wide'],
30596 ['xs', 'xs', 'sm'],
30597 ['xs', 'xs', 'xs'],
30603 ['sm', 'xs', 'xs'],
30607 ['wide', 'xs', 'xs', 'xs'],
30608 ['wide', 'xs', 'xs'],
30621 Roo.each(items, function(item, k){
30623 switch (item.size) {
30634 boxes.push([item]);
30658 var filterPattern = function(box, length)
30666 var pattern = box.slice(0, length);
30670 Roo.each(pattern, function(i){
30671 format.push(i.size);
30674 Roo.each(standard, function(s){
30676 if(String(s) != String(format)){
30685 if(!match && length == 1){
30690 filterPattern(box, length - 1);
30694 queue.push(pattern);
30696 box = box.slice(length, box.length);
30698 filterPattern(box, 4);
30704 Roo.each(boxes, function(box, k){
30710 if(box.length == 1){
30715 filterPattern(box, 4);
30722 var pos = this.el.getBox(true);
30726 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30728 var hit_end = false;
30730 Roo.each(queue, function(box){
30734 Roo.each(box, function(b){
30736 b.el.setVisibilityMode(Roo.Element.DISPLAY);
30746 Roo.each(box, function(b){
30748 b.el.setVisibilityMode(Roo.Element.DISPLAY);
30751 mx = Math.max(mx, b.x);
30755 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
30759 Roo.each(box, function(b){
30761 b.el.setVisibilityMode(Roo.Element.DISPLAY);
30775 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
30778 /** Sets position of item in DOM
30779 * @param {Element} item
30780 * @param {Number} x - horizontal position
30781 * @param {Number} y - vertical position
30782 * @param {Boolean} isInstant - disables transitions
30784 _processVerticalLayoutQueue : function( queue, isInstant )
30786 var pos = this.el.getBox(true);
30791 for (var i = 0; i < this.cols; i++){
30795 Roo.each(queue, function(box, k){
30797 var col = k % this.cols;
30799 Roo.each(box, function(b,kk){
30801 b.el.position('absolute');
30803 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30804 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30806 if(b.size == 'md-left' || b.size == 'md-right'){
30807 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30808 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30811 b.el.setWidth(width);
30812 b.el.setHeight(height);
30814 b.el.select('iframe',true).setSize(width,height);
30818 for (var i = 0; i < this.cols; i++){
30820 if(maxY[i] < maxY[col]){
30825 col = Math.min(col, i);
30829 x = pos.x + col * (this.colWidth + this.padWidth);
30833 var positions = [];
30835 switch (box.length){
30837 positions = this.getVerticalOneBoxColPositions(x, y, box);
30840 positions = this.getVerticalTwoBoxColPositions(x, y, box);
30843 positions = this.getVerticalThreeBoxColPositions(x, y, box);
30846 positions = this.getVerticalFourBoxColPositions(x, y, box);
30852 Roo.each(box, function(b,kk){
30854 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30856 var sz = b.el.getSize();
30858 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
30866 for (var i = 0; i < this.cols; i++){
30867 mY = Math.max(mY, maxY[i]);
30870 this.el.setHeight(mY - pos.y);
30874 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
30876 // var pos = this.el.getBox(true);
30879 // var maxX = pos.right;
30881 // var maxHeight = 0;
30883 // Roo.each(items, function(item, k){
30887 // item.el.position('absolute');
30889 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
30891 // item.el.setWidth(width);
30893 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
30895 // item.el.setHeight(height);
30898 // item.el.setXY([x, y], isInstant ? false : true);
30900 // item.el.setXY([maxX - width, y], isInstant ? false : true);
30903 // y = y + height + this.alternativePadWidth;
30905 // maxHeight = maxHeight + height + this.alternativePadWidth;
30909 // this.el.setHeight(maxHeight);
30913 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
30915 var pos = this.el.getBox(true);
30920 var maxX = pos.right;
30922 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
30924 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30926 Roo.each(queue, function(box, k){
30928 Roo.each(box, function(b, kk){
30930 b.el.position('absolute');
30932 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30933 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30935 if(b.size == 'md-left' || b.size == 'md-right'){
30936 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30937 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30940 b.el.setWidth(width);
30941 b.el.setHeight(height);
30949 var positions = [];
30951 switch (box.length){
30953 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
30956 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
30959 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
30962 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
30968 Roo.each(box, function(b,kk){
30970 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30972 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
30980 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
30982 Roo.each(eItems, function(b,k){
30984 b.size = (k == 0) ? 'sm' : 'xs';
30985 b.x = (k == 0) ? 2 : 1;
30986 b.y = (k == 0) ? 2 : 1;
30988 b.el.position('absolute');
30990 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30992 b.el.setWidth(width);
30994 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30996 b.el.setHeight(height);
31000 var positions = [];
31003 x : maxX - this.unitWidth * 2 - this.gutter,
31008 x : maxX - this.unitWidth,
31009 y : minY + (this.unitWidth + this.gutter) * 2
31013 x : maxX - this.unitWidth * 3 - this.gutter * 2,
31017 Roo.each(eItems, function(b,k){
31019 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31025 getVerticalOneBoxColPositions : function(x, y, box)
31029 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31031 if(box[0].size == 'md-left'){
31035 if(box[0].size == 'md-right'){
31040 x : x + (this.unitWidth + this.gutter) * rand,
31047 getVerticalTwoBoxColPositions : function(x, y, box)
31051 if(box[0].size == 'xs'){
31055 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31059 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31073 x : x + (this.unitWidth + this.gutter) * 2,
31074 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31081 getVerticalThreeBoxColPositions : function(x, y, box)
31085 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31093 x : x + (this.unitWidth + this.gutter) * 1,
31098 x : x + (this.unitWidth + this.gutter) * 2,
31106 if(box[0].size == 'xs' && box[1].size == 'xs'){
31115 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31119 x : x + (this.unitWidth + this.gutter) * 1,
31133 x : x + (this.unitWidth + this.gutter) * 2,
31138 x : x + (this.unitWidth + this.gutter) * 2,
31139 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31146 getVerticalFourBoxColPositions : function(x, y, box)
31150 if(box[0].size == 'xs'){
31159 y : y + (this.unitHeight + this.gutter) * 1
31164 y : y + (this.unitHeight + this.gutter) * 2
31168 x : x + (this.unitWidth + this.gutter) * 1,
31182 x : x + (this.unitWidth + this.gutter) * 2,
31187 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31188 y : y + (this.unitHeight + this.gutter) * 1
31192 x : x + (this.unitWidth + this.gutter) * 2,
31193 y : y + (this.unitWidth + this.gutter) * 2
31200 getHorizontalOneBoxColPositions : function(maxX, minY, box)
31204 if(box[0].size == 'md-left'){
31206 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31213 if(box[0].size == 'md-right'){
31215 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31216 y : minY + (this.unitWidth + this.gutter) * 1
31222 var rand = Math.floor(Math.random() * (4 - box[0].y));
31225 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31226 y : minY + (this.unitWidth + this.gutter) * rand
31233 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31237 if(box[0].size == 'xs'){
31240 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31245 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31246 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31254 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31259 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31260 y : minY + (this.unitWidth + this.gutter) * 2
31267 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31271 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31274 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31279 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31280 y : minY + (this.unitWidth + this.gutter) * 1
31284 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31285 y : minY + (this.unitWidth + this.gutter) * 2
31292 if(box[0].size == 'xs' && box[1].size == 'xs'){
31295 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31300 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31305 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31306 y : minY + (this.unitWidth + this.gutter) * 1
31314 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31319 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31320 y : minY + (this.unitWidth + this.gutter) * 2
31324 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31325 y : minY + (this.unitWidth + this.gutter) * 2
31332 getHorizontalFourBoxColPositions : function(maxX, minY, box)
31336 if(box[0].size == 'xs'){
31339 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31344 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31349 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),
31354 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31355 y : minY + (this.unitWidth + this.gutter) * 1
31363 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31368 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31369 y : minY + (this.unitWidth + this.gutter) * 2
31373 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31374 y : minY + (this.unitWidth + this.gutter) * 2
31378 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),
31379 y : minY + (this.unitWidth + this.gutter) * 2
31387 * remove a Masonry Brick
31388 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31390 removeBrick : function(brick_id)
31396 for (var i = 0; i<this.bricks.length; i++) {
31397 if (this.bricks[i].id == brick_id) {
31398 this.bricks.splice(i,1);
31399 this.el.dom.removeChild(Roo.get(brick_id).dom);
31406 * adds a Masonry Brick
31407 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31409 addBrick : function(cfg)
31411 var cn = new Roo.bootstrap.MasonryBrick(cfg);
31412 //this.register(cn);
31413 cn.parentId = this.id;
31414 cn.onRender(this.el, null);
31419 * register a Masonry Brick
31420 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31423 register : function(brick)
31425 this.bricks.push(brick);
31426 brick.masonryId = this.id;
31430 * clear all the Masonry Brick
31432 clearAll : function()
31435 //this.getChildContainer().dom.innerHTML = "";
31436 this.el.dom.innerHTML = '';
31439 getSelected : function()
31441 if (!this.selectedBrick) {
31445 return this.selectedBrick;
31449 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31453 * register a Masonry Layout
31454 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31457 register : function(layout)
31459 this.groups[layout.id] = layout;
31462 * fetch a Masonry Layout based on the masonry layout ID
31463 * @param {string} the masonry layout to add
31464 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31467 get: function(layout_id) {
31468 if (typeof(this.groups[layout_id]) == 'undefined') {
31471 return this.groups[layout_id] ;
31483 * http://masonry.desandro.com
31485 * The idea is to render all the bricks based on vertical width...
31487 * The original code extends 'outlayer' - we might need to use that....
31493 * @class Roo.bootstrap.LayoutMasonryAuto
31494 * @extends Roo.bootstrap.Component
31495 * Bootstrap Layout Masonry class
31498 * Create a new Element
31499 * @param {Object} config The config object
31502 Roo.bootstrap.LayoutMasonryAuto = function(config){
31503 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31506 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
31509 * @cfg {Boolean} isFitWidth - resize the width..
31511 isFitWidth : false, // options..
31513 * @cfg {Boolean} isOriginLeft = left align?
31515 isOriginLeft : true,
31517 * @cfg {Boolean} isOriginTop = top align?
31519 isOriginTop : false,
31521 * @cfg {Boolean} isLayoutInstant = no animation?
31523 isLayoutInstant : false, // needed?
31525 * @cfg {Boolean} isResizingContainer = not sure if this is used..
31527 isResizingContainer : true,
31529 * @cfg {Number} columnWidth width of the columns
31535 * @cfg {Number} maxCols maximum number of columns
31540 * @cfg {Number} padHeight padding below box..
31546 * @cfg {Boolean} isAutoInitial defalut true
31549 isAutoInitial : true,
31555 initialColumnWidth : 0,
31556 currentSize : null,
31558 colYs : null, // array.
31565 bricks: null, //CompositeElement
31566 cols : 0, // array?
31567 // element : null, // wrapped now this.el
31568 _isLayoutInited : null,
31571 getAutoCreate : function(){
31575 cls: 'blog-masonary-wrapper ' + this.cls,
31577 cls : 'mas-boxes masonary'
31584 getChildContainer: function( )
31586 if (this.boxesEl) {
31587 return this.boxesEl;
31590 this.boxesEl = this.el.select('.mas-boxes').first();
31592 return this.boxesEl;
31596 initEvents : function()
31600 if(this.isAutoInitial){
31601 Roo.log('hook children rendered');
31602 this.on('childrenrendered', function() {
31603 Roo.log('children rendered');
31610 initial : function()
31612 this.reloadItems();
31614 this.currentSize = this.el.getBox(true);
31616 /// was window resize... - let's see if this works..
31617 Roo.EventManager.onWindowResize(this.resize, this);
31619 if(!this.isAutoInitial){
31624 this.layout.defer(500,this);
31627 reloadItems: function()
31629 this.bricks = this.el.select('.masonry-brick', true);
31631 this.bricks.each(function(b) {
31632 //Roo.log(b.getSize());
31633 if (!b.attr('originalwidth')) {
31634 b.attr('originalwidth', b.getSize().width);
31639 Roo.log(this.bricks.elements.length);
31642 resize : function()
31645 var cs = this.el.getBox(true);
31647 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
31648 Roo.log("no change in with or X");
31651 this.currentSize = cs;
31655 layout : function()
31658 this._resetLayout();
31659 //this._manageStamps();
31661 // don't animate first layout
31662 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31663 this.layoutItems( isInstant );
31665 // flag for initalized
31666 this._isLayoutInited = true;
31669 layoutItems : function( isInstant )
31671 //var items = this._getItemsForLayout( this.items );
31672 // original code supports filtering layout items.. we just ignore it..
31674 this._layoutItems( this.bricks , isInstant );
31676 this._postLayout();
31678 _layoutItems : function ( items , isInstant)
31680 //this.fireEvent( 'layout', this, items );
31683 if ( !items || !items.elements.length ) {
31684 // no items, emit event with empty array
31689 items.each(function(item) {
31690 Roo.log("layout item");
31692 // get x/y object from method
31693 var position = this._getItemLayoutPosition( item );
31695 position.item = item;
31696 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
31697 queue.push( position );
31700 this._processLayoutQueue( queue );
31702 /** Sets position of item in DOM
31703 * @param {Element} item
31704 * @param {Number} x - horizontal position
31705 * @param {Number} y - vertical position
31706 * @param {Boolean} isInstant - disables transitions
31708 _processLayoutQueue : function( queue )
31710 for ( var i=0, len = queue.length; i < len; i++ ) {
31711 var obj = queue[i];
31712 obj.item.position('absolute');
31713 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
31719 * Any logic you want to do after each layout,
31720 * i.e. size the container
31722 _postLayout : function()
31724 this.resizeContainer();
31727 resizeContainer : function()
31729 if ( !this.isResizingContainer ) {
31732 var size = this._getContainerSize();
31734 this.el.setSize(size.width,size.height);
31735 this.boxesEl.setSize(size.width,size.height);
31741 _resetLayout : function()
31743 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
31744 this.colWidth = this.el.getWidth();
31745 //this.gutter = this.el.getWidth();
31747 this.measureColumns();
31753 this.colYs.push( 0 );
31759 measureColumns : function()
31761 this.getContainerWidth();
31762 // if columnWidth is 0, default to outerWidth of first item
31763 if ( !this.columnWidth ) {
31764 var firstItem = this.bricks.first();
31765 Roo.log(firstItem);
31766 this.columnWidth = this.containerWidth;
31767 if (firstItem && firstItem.attr('originalwidth') ) {
31768 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
31770 // columnWidth fall back to item of first element
31771 Roo.log("set column width?");
31772 this.initialColumnWidth = this.columnWidth ;
31774 // if first elem has no width, default to size of container
31779 if (this.initialColumnWidth) {
31780 this.columnWidth = this.initialColumnWidth;
31785 // column width is fixed at the top - however if container width get's smaller we should
31788 // this bit calcs how man columns..
31790 var columnWidth = this.columnWidth += this.gutter;
31792 // calculate columns
31793 var containerWidth = this.containerWidth + this.gutter;
31795 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
31796 // fix rounding errors, typically with gutters
31797 var excess = columnWidth - containerWidth % columnWidth;
31800 // if overshoot is less than a pixel, round up, otherwise floor it
31801 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
31802 cols = Math[ mathMethod ]( cols );
31803 this.cols = Math.max( cols, 1 );
31804 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31806 // padding positioning..
31807 var totalColWidth = this.cols * this.columnWidth;
31808 var padavail = this.containerWidth - totalColWidth;
31809 // so for 2 columns - we need 3 'pads'
31811 var padNeeded = (1+this.cols) * this.padWidth;
31813 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
31815 this.columnWidth += padExtra
31816 //this.padWidth = Math.floor(padavail / ( this.cols));
31818 // adjust colum width so that padding is fixed??
31820 // we have 3 columns ... total = width * 3
31821 // we have X left over... that should be used by
31823 //if (this.expandC) {
31831 getContainerWidth : function()
31833 /* // container is parent if fit width
31834 var container = this.isFitWidth ? this.element.parentNode : this.element;
31835 // check that this.size and size are there
31836 // IE8 triggers resize on body size change, so they might not be
31838 var size = getSize( container ); //FIXME
31839 this.containerWidth = size && size.innerWidth; //FIXME
31842 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
31846 _getItemLayoutPosition : function( item ) // what is item?
31848 // we resize the item to our columnWidth..
31850 item.setWidth(this.columnWidth);
31851 item.autoBoxAdjust = false;
31853 var sz = item.getSize();
31855 // how many columns does this brick span
31856 var remainder = this.containerWidth % this.columnWidth;
31858 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
31859 // round if off by 1 pixel, otherwise use ceil
31860 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
31861 colSpan = Math.min( colSpan, this.cols );
31863 // normally this should be '1' as we dont' currently allow multi width columns..
31865 var colGroup = this._getColGroup( colSpan );
31866 // get the minimum Y value from the columns
31867 var minimumY = Math.min.apply( Math, colGroup );
31868 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
31870 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
31872 // position the brick
31874 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
31875 y: this.currentSize.y + minimumY + this.padHeight
31879 // apply setHeight to necessary columns
31880 var setHeight = minimumY + sz.height + this.padHeight;
31881 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
31883 var setSpan = this.cols + 1 - colGroup.length;
31884 for ( var i = 0; i < setSpan; i++ ) {
31885 this.colYs[ shortColIndex + i ] = setHeight ;
31892 * @param {Number} colSpan - number of columns the element spans
31893 * @returns {Array} colGroup
31895 _getColGroup : function( colSpan )
31897 if ( colSpan < 2 ) {
31898 // if brick spans only one column, use all the column Ys
31903 // how many different places could this brick fit horizontally
31904 var groupCount = this.cols + 1 - colSpan;
31905 // for each group potential horizontal position
31906 for ( var i = 0; i < groupCount; i++ ) {
31907 // make an array of colY values for that one group
31908 var groupColYs = this.colYs.slice( i, i + colSpan );
31909 // and get the max value of the array
31910 colGroup[i] = Math.max.apply( Math, groupColYs );
31915 _manageStamp : function( stamp )
31917 var stampSize = stamp.getSize();
31918 var offset = stamp.getBox();
31919 // get the columns that this stamp affects
31920 var firstX = this.isOriginLeft ? offset.x : offset.right;
31921 var lastX = firstX + stampSize.width;
31922 var firstCol = Math.floor( firstX / this.columnWidth );
31923 firstCol = Math.max( 0, firstCol );
31925 var lastCol = Math.floor( lastX / this.columnWidth );
31926 // lastCol should not go over if multiple of columnWidth #425
31927 lastCol -= lastX % this.columnWidth ? 0 : 1;
31928 lastCol = Math.min( this.cols - 1, lastCol );
31930 // set colYs to bottom of the stamp
31931 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
31934 for ( var i = firstCol; i <= lastCol; i++ ) {
31935 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
31940 _getContainerSize : function()
31942 this.maxY = Math.max.apply( Math, this.colYs );
31947 if ( this.isFitWidth ) {
31948 size.width = this._getContainerFitWidth();
31954 _getContainerFitWidth : function()
31956 var unusedCols = 0;
31957 // count unused columns
31960 if ( this.colYs[i] !== 0 ) {
31965 // fit container to columns that have been used
31966 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
31969 needsResizeLayout : function()
31971 var previousWidth = this.containerWidth;
31972 this.getContainerWidth();
31973 return previousWidth !== this.containerWidth;
31988 * @class Roo.bootstrap.MasonryBrick
31989 * @extends Roo.bootstrap.Component
31990 * Bootstrap MasonryBrick class
31993 * Create a new MasonryBrick
31994 * @param {Object} config The config object
31997 Roo.bootstrap.MasonryBrick = function(config){
31999 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32001 Roo.bootstrap.MasonryBrick.register(this);
32007 * When a MasonryBrick is clcik
32008 * @param {Roo.bootstrap.MasonryBrick} this
32009 * @param {Roo.EventObject} e
32015 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
32018 * @cfg {String} title
32022 * @cfg {String} html
32026 * @cfg {String} bgimage
32030 * @cfg {String} videourl
32034 * @cfg {String} cls
32038 * @cfg {String} href
32042 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32047 * @cfg {String} placetitle (center|bottom)
32052 * @cfg {Boolean} isFitContainer defalut true
32054 isFitContainer : true,
32057 * @cfg {Boolean} preventDefault defalut false
32059 preventDefault : false,
32062 * @cfg {Boolean} inverse defalut false
32064 maskInverse : false,
32066 getAutoCreate : function()
32068 if(!this.isFitContainer){
32069 return this.getSplitAutoCreate();
32072 var cls = 'masonry-brick masonry-brick-full';
32074 if(this.href.length){
32075 cls += ' masonry-brick-link';
32078 if(this.bgimage.length){
32079 cls += ' masonry-brick-image';
32082 if(this.maskInverse){
32083 cls += ' mask-inverse';
32086 if(!this.html.length && !this.maskInverse && !this.videourl.length){
32087 cls += ' enable-mask';
32091 cls += ' masonry-' + this.size + '-brick';
32094 if(this.placetitle.length){
32096 switch (this.placetitle) {
32098 cls += ' masonry-center-title';
32101 cls += ' masonry-bottom-title';
32108 if(!this.html.length && !this.bgimage.length){
32109 cls += ' masonry-center-title';
32112 if(!this.html.length && this.bgimage.length){
32113 cls += ' masonry-bottom-title';
32118 cls += ' ' + this.cls;
32122 tag: (this.href.length) ? 'a' : 'div',
32127 cls: 'masonry-brick-mask'
32131 cls: 'masonry-brick-paragraph',
32137 if(this.href.length){
32138 cfg.href = this.href;
32141 var cn = cfg.cn[1].cn;
32143 if(this.title.length){
32146 cls: 'masonry-brick-title',
32151 if(this.html.length){
32154 cls: 'masonry-brick-text',
32159 if (!this.title.length && !this.html.length) {
32160 cfg.cn[1].cls += ' hide';
32163 if(this.bgimage.length){
32166 cls: 'masonry-brick-image-view',
32171 if(this.videourl.length){
32172 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32173 // youtube support only?
32176 cls: 'masonry-brick-image-view',
32179 allowfullscreen : true
32187 getSplitAutoCreate : function()
32189 var cls = 'masonry-brick masonry-brick-split';
32191 if(this.href.length){
32192 cls += ' masonry-brick-link';
32195 if(this.bgimage.length){
32196 cls += ' masonry-brick-image';
32200 cls += ' masonry-' + this.size + '-brick';
32203 switch (this.placetitle) {
32205 cls += ' masonry-center-title';
32208 cls += ' masonry-bottom-title';
32211 if(!this.bgimage.length){
32212 cls += ' masonry-center-title';
32215 if(this.bgimage.length){
32216 cls += ' masonry-bottom-title';
32222 cls += ' ' + this.cls;
32226 tag: (this.href.length) ? 'a' : 'div',
32231 cls: 'masonry-brick-split-head',
32235 cls: 'masonry-brick-paragraph',
32242 cls: 'masonry-brick-split-body',
32248 if(this.href.length){
32249 cfg.href = this.href;
32252 if(this.title.length){
32253 cfg.cn[0].cn[0].cn.push({
32255 cls: 'masonry-brick-title',
32260 if(this.html.length){
32261 cfg.cn[1].cn.push({
32263 cls: 'masonry-brick-text',
32268 if(this.bgimage.length){
32269 cfg.cn[0].cn.push({
32271 cls: 'masonry-brick-image-view',
32276 if(this.videourl.length){
32277 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32278 // youtube support only?
32279 cfg.cn[0].cn.cn.push({
32281 cls: 'masonry-brick-image-view',
32284 allowfullscreen : true
32291 initEvents: function()
32293 switch (this.size) {
32326 this.el.on('touchstart', this.onTouchStart, this);
32327 this.el.on('touchmove', this.onTouchMove, this);
32328 this.el.on('touchend', this.onTouchEnd, this);
32329 this.el.on('contextmenu', this.onContextMenu, this);
32331 this.el.on('mouseenter' ,this.enter, this);
32332 this.el.on('mouseleave', this.leave, this);
32333 this.el.on('click', this.onClick, this);
32336 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32337 this.parent().bricks.push(this);
32342 onClick: function(e, el)
32344 var time = this.endTimer - this.startTimer;
32345 // Roo.log(e.preventDefault());
32348 e.preventDefault();
32353 if(!this.preventDefault){
32357 e.preventDefault();
32359 if (this.activcClass != '') {
32360 this.selectBrick();
32363 this.fireEvent('click', this);
32366 enter: function(e, el)
32368 e.preventDefault();
32370 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32374 if(this.bgimage.length && this.html.length){
32375 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32379 leave: function(e, el)
32381 e.preventDefault();
32383 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32387 if(this.bgimage.length && this.html.length){
32388 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32392 onTouchStart: function(e, el)
32394 // e.preventDefault();
32396 this.touchmoved = false;
32398 if(!this.isFitContainer){
32402 if(!this.bgimage.length || !this.html.length){
32406 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32408 this.timer = new Date().getTime();
32412 onTouchMove: function(e, el)
32414 this.touchmoved = true;
32417 onContextMenu : function(e,el)
32419 e.preventDefault();
32420 e.stopPropagation();
32424 onTouchEnd: function(e, el)
32426 // e.preventDefault();
32428 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32435 if(!this.bgimage.length || !this.html.length){
32437 if(this.href.length){
32438 window.location.href = this.href;
32444 if(!this.isFitContainer){
32448 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32450 window.location.href = this.href;
32453 //selection on single brick only
32454 selectBrick : function() {
32456 if (!this.parentId) {
32460 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32461 var index = m.selectedBrick.indexOf(this.id);
32464 m.selectedBrick.splice(index,1);
32465 this.el.removeClass(this.activeClass);
32469 for(var i = 0; i < m.selectedBrick.length; i++) {
32470 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32471 b.el.removeClass(b.activeClass);
32474 m.selectedBrick = [];
32476 m.selectedBrick.push(this.id);
32477 this.el.addClass(this.activeClass);
32483 Roo.apply(Roo.bootstrap.MasonryBrick, {
32486 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32488 * register a Masonry Brick
32489 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32492 register : function(brick)
32494 //this.groups[brick.id] = brick;
32495 this.groups.add(brick.id, brick);
32498 * fetch a masonry brick based on the masonry brick ID
32499 * @param {string} the masonry brick to add
32500 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
32503 get: function(brick_id)
32505 // if (typeof(this.groups[brick_id]) == 'undefined') {
32508 // return this.groups[brick_id] ;
32510 if(this.groups.key(brick_id)) {
32511 return this.groups.key(brick_id);
32529 * @class Roo.bootstrap.Brick
32530 * @extends Roo.bootstrap.Component
32531 * Bootstrap Brick class
32534 * Create a new Brick
32535 * @param {Object} config The config object
32538 Roo.bootstrap.Brick = function(config){
32539 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
32545 * When a Brick is click
32546 * @param {Roo.bootstrap.Brick} this
32547 * @param {Roo.EventObject} e
32553 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
32556 * @cfg {String} title
32560 * @cfg {String} html
32564 * @cfg {String} bgimage
32568 * @cfg {String} cls
32572 * @cfg {String} href
32576 * @cfg {String} video
32580 * @cfg {Boolean} square
32584 getAutoCreate : function()
32586 var cls = 'roo-brick';
32588 if(this.href.length){
32589 cls += ' roo-brick-link';
32592 if(this.bgimage.length){
32593 cls += ' roo-brick-image';
32596 if(!this.html.length && !this.bgimage.length){
32597 cls += ' roo-brick-center-title';
32600 if(!this.html.length && this.bgimage.length){
32601 cls += ' roo-brick-bottom-title';
32605 cls += ' ' + this.cls;
32609 tag: (this.href.length) ? 'a' : 'div',
32614 cls: 'roo-brick-paragraph',
32620 if(this.href.length){
32621 cfg.href = this.href;
32624 var cn = cfg.cn[0].cn;
32626 if(this.title.length){
32629 cls: 'roo-brick-title',
32634 if(this.html.length){
32637 cls: 'roo-brick-text',
32644 if(this.bgimage.length){
32647 cls: 'roo-brick-image-view',
32655 initEvents: function()
32657 if(this.title.length || this.html.length){
32658 this.el.on('mouseenter' ,this.enter, this);
32659 this.el.on('mouseleave', this.leave, this);
32662 Roo.EventManager.onWindowResize(this.resize, this);
32664 if(this.bgimage.length){
32665 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
32666 this.imageEl.on('load', this.onImageLoad, this);
32673 onImageLoad : function()
32678 resize : function()
32680 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
32682 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
32684 if(this.bgimage.length){
32685 var image = this.el.select('.roo-brick-image-view', true).first();
32687 image.setWidth(paragraph.getWidth());
32690 image.setHeight(paragraph.getWidth());
32693 this.el.setHeight(image.getHeight());
32694 paragraph.setHeight(image.getHeight());
32700 enter: function(e, el)
32702 e.preventDefault();
32704 if(this.bgimage.length){
32705 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
32706 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
32710 leave: function(e, el)
32712 e.preventDefault();
32714 if(this.bgimage.length){
32715 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
32716 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
32732 * @class Roo.bootstrap.NumberField
32733 * @extends Roo.bootstrap.Input
32734 * Bootstrap NumberField class
32740 * Create a new NumberField
32741 * @param {Object} config The config object
32744 Roo.bootstrap.NumberField = function(config){
32745 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
32748 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
32751 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
32753 allowDecimals : true,
32755 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
32757 decimalSeparator : ".",
32759 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
32761 decimalPrecision : 2,
32763 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
32765 allowNegative : true,
32767 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
32769 minValue : Number.NEGATIVE_INFINITY,
32771 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
32773 maxValue : Number.MAX_VALUE,
32775 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
32777 minText : "The minimum value for this field is {0}",
32779 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
32781 maxText : "The maximum value for this field is {0}",
32783 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
32784 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
32786 nanText : "{0} is not a valid number",
32788 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
32793 initEvents : function()
32795 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
32797 var allowed = "0123456789";
32799 if(this.allowDecimals){
32800 allowed += this.decimalSeparator;
32803 if(this.allowNegative){
32807 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
32809 var keyPress = function(e){
32811 var k = e.getKey();
32813 var c = e.getCharCode();
32816 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
32817 allowed.indexOf(String.fromCharCode(c)) === -1
32823 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
32827 if(allowed.indexOf(String.fromCharCode(c)) === -1){
32832 this.el.on("keypress", keyPress, this);
32835 validateValue : function(value)
32838 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
32842 var num = this.parseValue(value);
32845 this.markInvalid(String.format(this.nanText, value));
32849 if(num < this.minValue){
32850 this.markInvalid(String.format(this.minText, this.minValue));
32854 if(num > this.maxValue){
32855 this.markInvalid(String.format(this.maxText, this.maxValue));
32862 getValue : function()
32864 return this.fixPrecision(this.parseValue(Roo.bootstrap.NumberField.superclass.getValue.call(this)));
32867 parseValue : function(value)
32869 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
32870 return isNaN(value) ? '' : value;
32873 fixPrecision : function(value)
32875 var nan = isNaN(value);
32877 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
32878 return nan ? '' : value;
32880 return parseFloat(value).toFixed(this.decimalPrecision);
32883 setValue : function(v)
32885 v = this.fixPrecision(v);
32886 Roo.bootstrap.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
32889 decimalPrecisionFcn : function(v)
32891 return Math.floor(v);
32894 beforeBlur : function()
32900 var v = this.parseValue(this.getRawValue());
32915 * @class Roo.bootstrap.DocumentSlider
32916 * @extends Roo.bootstrap.Component
32917 * Bootstrap DocumentSlider class
32920 * Create a new DocumentViewer
32921 * @param {Object} config The config object
32924 Roo.bootstrap.DocumentSlider = function(config){
32925 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
32932 * Fire after initEvent
32933 * @param {Roo.bootstrap.DocumentSlider} this
32938 * Fire after update
32939 * @param {Roo.bootstrap.DocumentSlider} this
32945 * @param {Roo.bootstrap.DocumentSlider} this
32951 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
32957 getAutoCreate : function()
32961 cls : 'roo-document-slider',
32965 cls : 'roo-document-slider-header',
32969 cls : 'roo-document-slider-header-title'
32975 cls : 'roo-document-slider-body',
32979 cls : 'roo-document-slider-prev',
32983 cls : 'fa fa-chevron-left'
32989 cls : 'roo-document-slider-thumb',
32993 cls : 'roo-document-slider-image'
32999 cls : 'roo-document-slider-next',
33003 cls : 'fa fa-chevron-right'
33015 initEvents : function()
33017 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33018 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33020 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33021 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33023 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33024 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33026 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33027 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33029 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33030 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33032 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33033 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33035 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33036 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33038 this.thumbEl.on('click', this.onClick, this);
33040 this.prevIndicator.on('click', this.prev, this);
33042 this.nextIndicator.on('click', this.next, this);
33046 initial : function()
33048 if(this.files.length){
33049 this.indicator = 1;
33053 this.fireEvent('initial', this);
33056 update : function()
33058 this.imageEl.attr('src', this.files[this.indicator - 1]);
33060 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33062 this.prevIndicator.show();
33064 if(this.indicator == 1){
33065 this.prevIndicator.hide();
33068 this.nextIndicator.show();
33070 if(this.indicator == this.files.length){
33071 this.nextIndicator.hide();
33074 this.thumbEl.scrollTo('top');
33076 this.fireEvent('update', this);
33079 onClick : function(e)
33081 e.preventDefault();
33083 this.fireEvent('click', this);
33088 e.preventDefault();
33090 this.indicator = Math.max(1, this.indicator - 1);
33097 e.preventDefault();
33099 this.indicator = Math.min(this.files.length, this.indicator + 1);
33113 * @class Roo.bootstrap.RadioSet
33114 * @extends Roo.bootstrap.Input
33115 * Bootstrap RadioSet class
33116 * @cfg {String} indicatorpos (left|right) default left
33117 * @cfg {Boolean} inline (true|false) inline the element (default true)
33118 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33120 * Create a new RadioSet
33121 * @param {Object} config The config object
33124 Roo.bootstrap.RadioSet = function(config){
33126 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33130 Roo.bootstrap.RadioSet.register(this);
33135 * Fires when the element is checked or unchecked.
33136 * @param {Roo.bootstrap.RadioSet} this This radio
33137 * @param {Roo.bootstrap.Radio} item The checked item
33144 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
33152 indicatorpos : 'left',
33154 getAutoCreate : function()
33158 cls : 'roo-radio-set-label',
33162 html : this.fieldLabel
33167 if(this.indicatorpos == 'left'){
33170 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33171 tooltip : 'This field is required'
33176 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33177 tooltip : 'This field is required'
33183 cls : 'roo-radio-set-items'
33186 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33188 if (align === 'left' && this.fieldLabel.length) {
33191 cls : "roo-radio-set-right",
33197 if(this.labelWidth > 12){
33198 label.style = "width: " + this.labelWidth + 'px';
33201 if(this.labelWidth < 13 && this.labelmd == 0){
33202 this.labelmd = this.labelWidth;
33205 if(this.labellg > 0){
33206 label.cls += ' col-lg-' + this.labellg;
33207 items.cls += ' col-lg-' + (12 - this.labellg);
33210 if(this.labelmd > 0){
33211 label.cls += ' col-md-' + this.labelmd;
33212 items.cls += ' col-md-' + (12 - this.labelmd);
33215 if(this.labelsm > 0){
33216 label.cls += ' col-sm-' + this.labelsm;
33217 items.cls += ' col-sm-' + (12 - this.labelsm);
33220 if(this.labelxs > 0){
33221 label.cls += ' col-xs-' + this.labelxs;
33222 items.cls += ' col-xs-' + (12 - this.labelxs);
33228 cls : 'roo-radio-set',
33232 cls : 'roo-radio-set-input',
33235 value : this.value ? this.value : ''
33242 if(this.weight.length){
33243 cfg.cls += ' roo-radio-' + this.weight;
33247 cfg.cls += ' roo-radio-set-inline';
33254 initEvents : function()
33256 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33257 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33259 if(!this.fieldLabel.length){
33260 this.labelEl.hide();
33263 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33264 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33266 this.indicatorEl().setVisibilityMode(Roo.Element.DISPLAY);
33267 this.indicatorEl().hide();
33269 this.originalValue = this.getValue();
33273 inputEl: function ()
33275 return this.el.select('.roo-radio-set-input', true).first();
33278 getChildContainer : function()
33280 return this.itemsEl;
33283 register : function(item)
33285 this.radioes.push(item);
33289 validate : function()
33293 Roo.each(this.radioes, function(i){
33302 if(this.allowBlank) {
33306 if(this.disabled || valid){
33311 this.markInvalid();
33316 markValid : function()
33318 if(this.labelEl.isVisible(true)){
33319 this.indicatorEl().hide();
33322 this.el.removeClass([this.invalidClass, this.validClass]);
33323 this.el.addClass(this.validClass);
33325 this.fireEvent('valid', this);
33328 markInvalid : function(msg)
33330 if(this.allowBlank || this.disabled){
33334 if(this.labelEl.isVisible(true)){
33335 this.indicatorEl().show();
33338 this.el.removeClass([this.invalidClass, this.validClass]);
33339 this.el.addClass(this.invalidClass);
33341 this.fireEvent('invalid', this, msg);
33345 setValue : function(v, suppressEvent)
33349 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33352 Roo.each(this.radioes, function(i){
33355 i.el.removeClass('checked');
33357 if(i.value === v || i.value.toString() === v.toString()){
33359 i.el.addClass('checked');
33361 if(suppressEvent !== true){
33362 this.fireEvent('check', this, i);
33371 clearInvalid : function(){
33373 if(!this.el || this.preventMark){
33377 this.el.removeClass([this.invalidClass]);
33379 this.fireEvent('valid', this);
33384 Roo.apply(Roo.bootstrap.RadioSet, {
33388 register : function(set)
33390 this.groups[set.name] = set;
33393 get: function(name)
33395 if (typeof(this.groups[name]) == 'undefined') {
33399 return this.groups[name] ;
33405 * Ext JS Library 1.1.1
33406 * Copyright(c) 2006-2007, Ext JS, LLC.
33408 * Originally Released Under LGPL - original licence link has changed is not relivant.
33411 * <script type="text/javascript">
33416 * @class Roo.bootstrap.SplitBar
33417 * @extends Roo.util.Observable
33418 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
33422 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
33423 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
33424 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
33425 split.minSize = 100;
33426 split.maxSize = 600;
33427 split.animate = true;
33428 split.on('moved', splitterMoved);
33431 * Create a new SplitBar
33432 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
33433 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
33434 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33435 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
33436 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
33437 position of the SplitBar).
33439 Roo.bootstrap.SplitBar = function(cfg){
33444 // dragElement : elm
33445 // resizingElement: el,
33447 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
33448 // placement : Roo.bootstrap.SplitBar.LEFT ,
33449 // existingProxy ???
33452 this.el = Roo.get(cfg.dragElement, true);
33453 this.el.dom.unselectable = "on";
33455 this.resizingEl = Roo.get(cfg.resizingElement, true);
33459 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33460 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
33463 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
33466 * The minimum size of the resizing element. (Defaults to 0)
33472 * The maximum size of the resizing element. (Defaults to 2000)
33475 this.maxSize = 2000;
33478 * Whether to animate the transition to the new size
33481 this.animate = false;
33484 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
33487 this.useShim = false;
33492 if(!cfg.existingProxy){
33494 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
33496 this.proxy = Roo.get(cfg.existingProxy).dom;
33499 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
33502 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
33505 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
33508 this.dragSpecs = {};
33511 * @private The adapter to use to positon and resize elements
33513 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33514 this.adapter.init(this);
33516 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33518 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
33519 this.el.addClass("roo-splitbar-h");
33522 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
33523 this.el.addClass("roo-splitbar-v");
33529 * Fires when the splitter is moved (alias for {@link #event-moved})
33530 * @param {Roo.bootstrap.SplitBar} this
33531 * @param {Number} newSize the new width or height
33536 * Fires when the splitter is moved
33537 * @param {Roo.bootstrap.SplitBar} this
33538 * @param {Number} newSize the new width or height
33542 * @event beforeresize
33543 * Fires before the splitter is dragged
33544 * @param {Roo.bootstrap.SplitBar} this
33546 "beforeresize" : true,
33548 "beforeapply" : true
33551 Roo.util.Observable.call(this);
33554 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
33555 onStartProxyDrag : function(x, y){
33556 this.fireEvent("beforeresize", this);
33558 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
33560 o.enableDisplayMode("block");
33561 // all splitbars share the same overlay
33562 Roo.bootstrap.SplitBar.prototype.overlay = o;
33564 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33565 this.overlay.show();
33566 Roo.get(this.proxy).setDisplayed("block");
33567 var size = this.adapter.getElementSize(this);
33568 this.activeMinSize = this.getMinimumSize();;
33569 this.activeMaxSize = this.getMaximumSize();;
33570 var c1 = size - this.activeMinSize;
33571 var c2 = Math.max(this.activeMaxSize - size, 0);
33572 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33573 this.dd.resetConstraints();
33574 this.dd.setXConstraint(
33575 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
33576 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
33578 this.dd.setYConstraint(0, 0);
33580 this.dd.resetConstraints();
33581 this.dd.setXConstraint(0, 0);
33582 this.dd.setYConstraint(
33583 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
33584 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
33587 this.dragSpecs.startSize = size;
33588 this.dragSpecs.startPoint = [x, y];
33589 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
33593 * @private Called after the drag operation by the DDProxy
33595 onEndProxyDrag : function(e){
33596 Roo.get(this.proxy).setDisplayed(false);
33597 var endPoint = Roo.lib.Event.getXY(e);
33599 this.overlay.hide();
33602 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33603 newSize = this.dragSpecs.startSize +
33604 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
33605 endPoint[0] - this.dragSpecs.startPoint[0] :
33606 this.dragSpecs.startPoint[0] - endPoint[0]
33609 newSize = this.dragSpecs.startSize +
33610 (this.placement == Roo.bootstrap.SplitBar.TOP ?
33611 endPoint[1] - this.dragSpecs.startPoint[1] :
33612 this.dragSpecs.startPoint[1] - endPoint[1]
33615 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
33616 if(newSize != this.dragSpecs.startSize){
33617 if(this.fireEvent('beforeapply', this, newSize) !== false){
33618 this.adapter.setElementSize(this, newSize);
33619 this.fireEvent("moved", this, newSize);
33620 this.fireEvent("resize", this, newSize);
33626 * Get the adapter this SplitBar uses
33627 * @return The adapter object
33629 getAdapter : function(){
33630 return this.adapter;
33634 * Set the adapter this SplitBar uses
33635 * @param {Object} adapter A SplitBar adapter object
33637 setAdapter : function(adapter){
33638 this.adapter = adapter;
33639 this.adapter.init(this);
33643 * Gets the minimum size for the resizing element
33644 * @return {Number} The minimum size
33646 getMinimumSize : function(){
33647 return this.minSize;
33651 * Sets the minimum size for the resizing element
33652 * @param {Number} minSize The minimum size
33654 setMinimumSize : function(minSize){
33655 this.minSize = minSize;
33659 * Gets the maximum size for the resizing element
33660 * @return {Number} The maximum size
33662 getMaximumSize : function(){
33663 return this.maxSize;
33667 * Sets the maximum size for the resizing element
33668 * @param {Number} maxSize The maximum size
33670 setMaximumSize : function(maxSize){
33671 this.maxSize = maxSize;
33675 * Sets the initialize size for the resizing element
33676 * @param {Number} size The initial size
33678 setCurrentSize : function(size){
33679 var oldAnimate = this.animate;
33680 this.animate = false;
33681 this.adapter.setElementSize(this, size);
33682 this.animate = oldAnimate;
33686 * Destroy this splitbar.
33687 * @param {Boolean} removeEl True to remove the element
33689 destroy : function(removeEl){
33691 this.shim.remove();
33694 this.proxy.parentNode.removeChild(this.proxy);
33702 * @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.
33704 Roo.bootstrap.SplitBar.createProxy = function(dir){
33705 var proxy = new Roo.Element(document.createElement("div"));
33706 proxy.unselectable();
33707 var cls = 'roo-splitbar-proxy';
33708 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
33709 document.body.appendChild(proxy.dom);
33714 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
33715 * Default Adapter. It assumes the splitter and resizing element are not positioned
33716 * elements and only gets/sets the width of the element. Generally used for table based layouts.
33718 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
33721 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
33722 // do nothing for now
33723 init : function(s){
33727 * Called before drag operations to get the current size of the resizing element.
33728 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33730 getElementSize : function(s){
33731 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33732 return s.resizingEl.getWidth();
33734 return s.resizingEl.getHeight();
33739 * Called after drag operations to set the size of the resizing element.
33740 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33741 * @param {Number} newSize The new size to set
33742 * @param {Function} onComplete A function to be invoked when resizing is complete
33744 setElementSize : function(s, newSize, onComplete){
33745 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33747 s.resizingEl.setWidth(newSize);
33749 onComplete(s, newSize);
33752 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
33757 s.resizingEl.setHeight(newSize);
33759 onComplete(s, newSize);
33762 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
33769 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
33770 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
33771 * Adapter that moves the splitter element to align with the resized sizing element.
33772 * Used with an absolute positioned SplitBar.
33773 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
33774 * document.body, make sure you assign an id to the body element.
33776 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
33777 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33778 this.container = Roo.get(container);
33781 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
33782 init : function(s){
33783 this.basic.init(s);
33786 getElementSize : function(s){
33787 return this.basic.getElementSize(s);
33790 setElementSize : function(s, newSize, onComplete){
33791 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
33794 moveSplitter : function(s){
33795 var yes = Roo.bootstrap.SplitBar;
33796 switch(s.placement){
33798 s.el.setX(s.resizingEl.getRight());
33801 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
33804 s.el.setY(s.resizingEl.getBottom());
33807 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
33814 * Orientation constant - Create a vertical SplitBar
33818 Roo.bootstrap.SplitBar.VERTICAL = 1;
33821 * Orientation constant - Create a horizontal SplitBar
33825 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
33828 * Placement constant - The resizing element is to the left of the splitter element
33832 Roo.bootstrap.SplitBar.LEFT = 1;
33835 * Placement constant - The resizing element is to the right of the splitter element
33839 Roo.bootstrap.SplitBar.RIGHT = 2;
33842 * Placement constant - The resizing element is positioned above the splitter element
33846 Roo.bootstrap.SplitBar.TOP = 3;
33849 * Placement constant - The resizing element is positioned under splitter element
33853 Roo.bootstrap.SplitBar.BOTTOM = 4;
33854 Roo.namespace("Roo.bootstrap.layout");/*
33856 * Ext JS Library 1.1.1
33857 * Copyright(c) 2006-2007, Ext JS, LLC.
33859 * Originally Released Under LGPL - original licence link has changed is not relivant.
33862 * <script type="text/javascript">
33866 * @class Roo.bootstrap.layout.Manager
33867 * @extends Roo.bootstrap.Component
33868 * Base class for layout managers.
33870 Roo.bootstrap.layout.Manager = function(config)
33872 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
33878 /** false to disable window resize monitoring @type Boolean */
33879 this.monitorWindowResize = true;
33884 * Fires when a layout is performed.
33885 * @param {Roo.LayoutManager} this
33889 * @event regionresized
33890 * Fires when the user resizes a region.
33891 * @param {Roo.LayoutRegion} region The resized region
33892 * @param {Number} newSize The new size (width for east/west, height for north/south)
33894 "regionresized" : true,
33896 * @event regioncollapsed
33897 * Fires when a region is collapsed.
33898 * @param {Roo.LayoutRegion} region The collapsed region
33900 "regioncollapsed" : true,
33902 * @event regionexpanded
33903 * Fires when a region is expanded.
33904 * @param {Roo.LayoutRegion} region The expanded region
33906 "regionexpanded" : true
33908 this.updating = false;
33911 this.el = Roo.get(config.el);
33917 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
33922 monitorWindowResize : true,
33928 onRender : function(ct, position)
33931 this.el = Roo.get(ct);
33934 //this.fireEvent('render',this);
33938 initEvents: function()
33942 // ie scrollbar fix
33943 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
33944 document.body.scroll = "no";
33945 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
33946 this.el.position('relative');
33948 this.id = this.el.id;
33949 this.el.addClass("roo-layout-container");
33950 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
33951 if(this.el.dom != document.body ) {
33952 this.el.on('resize', this.layout,this);
33953 this.el.on('show', this.layout,this);
33959 * Returns true if this layout is currently being updated
33960 * @return {Boolean}
33962 isUpdating : function(){
33963 return this.updating;
33967 * Suspend the LayoutManager from doing auto-layouts while
33968 * making multiple add or remove calls
33970 beginUpdate : function(){
33971 this.updating = true;
33975 * Restore auto-layouts and optionally disable the manager from performing a layout
33976 * @param {Boolean} noLayout true to disable a layout update
33978 endUpdate : function(noLayout){
33979 this.updating = false;
33985 layout: function(){
33989 onRegionResized : function(region, newSize){
33990 this.fireEvent("regionresized", region, newSize);
33994 onRegionCollapsed : function(region){
33995 this.fireEvent("regioncollapsed", region);
33998 onRegionExpanded : function(region){
33999 this.fireEvent("regionexpanded", region);
34003 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34004 * performs box-model adjustments.
34005 * @return {Object} The size as an object {width: (the width), height: (the height)}
34007 getViewSize : function()
34010 if(this.el.dom != document.body){
34011 size = this.el.getSize();
34013 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34015 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34016 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34021 * Returns the Element this layout is bound to.
34022 * @return {Roo.Element}
34024 getEl : function(){
34029 * Returns the specified region.
34030 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34031 * @return {Roo.LayoutRegion}
34033 getRegion : function(target){
34034 return this.regions[target.toLowerCase()];
34037 onWindowResize : function(){
34038 if(this.monitorWindowResize){
34045 * Ext JS Library 1.1.1
34046 * Copyright(c) 2006-2007, Ext JS, LLC.
34048 * Originally Released Under LGPL - original licence link has changed is not relivant.
34051 * <script type="text/javascript">
34054 * @class Roo.bootstrap.layout.Border
34055 * @extends Roo.bootstrap.layout.Manager
34056 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34057 * please see: examples/bootstrap/nested.html<br><br>
34059 <b>The container the layout is rendered into can be either the body element or any other element.
34060 If it is not the body element, the container needs to either be an absolute positioned element,
34061 or you will need to add "position:relative" to the css of the container. You will also need to specify
34062 the container size if it is not the body element.</b>
34065 * Create a new Border
34066 * @param {Object} config Configuration options
34068 Roo.bootstrap.layout.Border = function(config){
34069 config = config || {};
34070 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34074 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34075 if(config[region]){
34076 config[region].region = region;
34077 this.addRegion(config[region]);
34083 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
34085 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34087 * Creates and adds a new region if it doesn't already exist.
34088 * @param {String} target The target region key (north, south, east, west or center).
34089 * @param {Object} config The regions config object
34090 * @return {BorderLayoutRegion} The new region
34092 addRegion : function(config)
34094 if(!this.regions[config.region]){
34095 var r = this.factory(config);
34096 this.bindRegion(r);
34098 return this.regions[config.region];
34102 bindRegion : function(r){
34103 this.regions[r.config.region] = r;
34105 r.on("visibilitychange", this.layout, this);
34106 r.on("paneladded", this.layout, this);
34107 r.on("panelremoved", this.layout, this);
34108 r.on("invalidated", this.layout, this);
34109 r.on("resized", this.onRegionResized, this);
34110 r.on("collapsed", this.onRegionCollapsed, this);
34111 r.on("expanded", this.onRegionExpanded, this);
34115 * Performs a layout update.
34117 layout : function()
34119 if(this.updating) {
34123 // render all the rebions if they have not been done alreayd?
34124 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34125 if(this.regions[region] && !this.regions[region].bodyEl){
34126 this.regions[region].onRender(this.el)
34130 var size = this.getViewSize();
34131 var w = size.width;
34132 var h = size.height;
34137 //var x = 0, y = 0;
34139 var rs = this.regions;
34140 var north = rs["north"];
34141 var south = rs["south"];
34142 var west = rs["west"];
34143 var east = rs["east"];
34144 var center = rs["center"];
34145 //if(this.hideOnLayout){ // not supported anymore
34146 //c.el.setStyle("display", "none");
34148 if(north && north.isVisible()){
34149 var b = north.getBox();
34150 var m = north.getMargins();
34151 b.width = w - (m.left+m.right);
34154 centerY = b.height + b.y + m.bottom;
34155 centerH -= centerY;
34156 north.updateBox(this.safeBox(b));
34158 if(south && south.isVisible()){
34159 var b = south.getBox();
34160 var m = south.getMargins();
34161 b.width = w - (m.left+m.right);
34163 var totalHeight = (b.height + m.top + m.bottom);
34164 b.y = h - totalHeight + m.top;
34165 centerH -= totalHeight;
34166 south.updateBox(this.safeBox(b));
34168 if(west && west.isVisible()){
34169 var b = west.getBox();
34170 var m = west.getMargins();
34171 b.height = centerH - (m.top+m.bottom);
34173 b.y = centerY + m.top;
34174 var totalWidth = (b.width + m.left + m.right);
34175 centerX += totalWidth;
34176 centerW -= totalWidth;
34177 west.updateBox(this.safeBox(b));
34179 if(east && east.isVisible()){
34180 var b = east.getBox();
34181 var m = east.getMargins();
34182 b.height = centerH - (m.top+m.bottom);
34183 var totalWidth = (b.width + m.left + m.right);
34184 b.x = w - totalWidth + m.left;
34185 b.y = centerY + m.top;
34186 centerW -= totalWidth;
34187 east.updateBox(this.safeBox(b));
34190 var m = center.getMargins();
34192 x: centerX + m.left,
34193 y: centerY + m.top,
34194 width: centerW - (m.left+m.right),
34195 height: centerH - (m.top+m.bottom)
34197 //if(this.hideOnLayout){
34198 //center.el.setStyle("display", "block");
34200 center.updateBox(this.safeBox(centerBox));
34203 this.fireEvent("layout", this);
34207 safeBox : function(box){
34208 box.width = Math.max(0, box.width);
34209 box.height = Math.max(0, box.height);
34214 * Adds a ContentPanel (or subclass) to this layout.
34215 * @param {String} target The target region key (north, south, east, west or center).
34216 * @param {Roo.ContentPanel} panel The panel to add
34217 * @return {Roo.ContentPanel} The added panel
34219 add : function(target, panel){
34221 target = target.toLowerCase();
34222 return this.regions[target].add(panel);
34226 * Remove a ContentPanel (or subclass) to this layout.
34227 * @param {String} target The target region key (north, south, east, west or center).
34228 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34229 * @return {Roo.ContentPanel} The removed panel
34231 remove : function(target, panel){
34232 target = target.toLowerCase();
34233 return this.regions[target].remove(panel);
34237 * Searches all regions for a panel with the specified id
34238 * @param {String} panelId
34239 * @return {Roo.ContentPanel} The panel or null if it wasn't found
34241 findPanel : function(panelId){
34242 var rs = this.regions;
34243 for(var target in rs){
34244 if(typeof rs[target] != "function"){
34245 var p = rs[target].getPanel(panelId);
34255 * Searches all regions for a panel with the specified id and activates (shows) it.
34256 * @param {String/ContentPanel} panelId The panels id or the panel itself
34257 * @return {Roo.ContentPanel} The shown panel or null
34259 showPanel : function(panelId) {
34260 var rs = this.regions;
34261 for(var target in rs){
34262 var r = rs[target];
34263 if(typeof r != "function"){
34264 if(r.hasPanel(panelId)){
34265 return r.showPanel(panelId);
34273 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34274 * @param {Roo.state.Provider} provider (optional) An alternate state provider
34277 restoreState : function(provider){
34279 provider = Roo.state.Manager;
34281 var sm = new Roo.LayoutStateManager();
34282 sm.init(this, provider);
34288 * Adds a xtype elements to the layout.
34292 xtype : 'ContentPanel',
34299 xtype : 'NestedLayoutPanel',
34305 items : [ ... list of content panels or nested layout panels.. ]
34309 * @param {Object} cfg Xtype definition of item to add.
34311 addxtype : function(cfg)
34313 // basically accepts a pannel...
34314 // can accept a layout region..!?!?
34315 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34318 // theory? children can only be panels??
34320 //if (!cfg.xtype.match(/Panel$/)) {
34325 if (typeof(cfg.region) == 'undefined') {
34326 Roo.log("Failed to add Panel, region was not set");
34330 var region = cfg.region;
34336 xitems = cfg.items;
34343 case 'Content': // ContentPanel (el, cfg)
34344 case 'Scroll': // ContentPanel (el, cfg)
34346 cfg.autoCreate = true;
34347 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34349 // var el = this.el.createChild();
34350 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34353 this.add(region, ret);
34357 case 'TreePanel': // our new panel!
34358 cfg.el = this.el.createChild();
34359 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34360 this.add(region, ret);
34365 // create a new Layout (which is a Border Layout...
34367 var clayout = cfg.layout;
34368 clayout.el = this.el.createChild();
34369 clayout.items = clayout.items || [];
34373 // replace this exitems with the clayout ones..
34374 xitems = clayout.items;
34376 // force background off if it's in center...
34377 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34378 cfg.background = false;
34380 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
34383 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34384 //console.log('adding nested layout panel ' + cfg.toSource());
34385 this.add(region, ret);
34386 nb = {}; /// find first...
34391 // needs grid and region
34393 //var el = this.getRegion(region).el.createChild();
34395 *var el = this.el.createChild();
34396 // create the grid first...
34397 cfg.grid.container = el;
34398 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
34401 if (region == 'center' && this.active ) {
34402 cfg.background = false;
34405 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34407 this.add(region, ret);
34409 if (cfg.background) {
34410 // render grid on panel activation (if panel background)
34411 ret.on('activate', function(gp) {
34412 if (!gp.grid.rendered) {
34413 // gp.grid.render(el);
34417 // cfg.grid.render(el);
34423 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
34424 // it was the old xcomponent building that caused this before.
34425 // espeically if border is the top element in the tree.
34435 if (typeof(Roo[cfg.xtype]) != 'undefined') {
34437 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34438 this.add(region, ret);
34442 throw "Can not add '" + cfg.xtype + "' to Border";
34448 this.beginUpdate();
34452 Roo.each(xitems, function(i) {
34453 region = nb && i.region ? i.region : false;
34455 var add = ret.addxtype(i);
34458 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
34459 if (!i.background) {
34460 abn[region] = nb[region] ;
34467 // make the last non-background panel active..
34468 //if (nb) { Roo.log(abn); }
34471 for(var r in abn) {
34472 region = this.getRegion(r);
34474 // tried using nb[r], but it does not work..
34476 region.showPanel(abn[r]);
34487 factory : function(cfg)
34490 var validRegions = Roo.bootstrap.layout.Border.regions;
34492 var target = cfg.region;
34495 var r = Roo.bootstrap.layout;
34499 return new r.North(cfg);
34501 return new r.South(cfg);
34503 return new r.East(cfg);
34505 return new r.West(cfg);
34507 return new r.Center(cfg);
34509 throw 'Layout region "'+target+'" not supported.';
34516 * Ext JS Library 1.1.1
34517 * Copyright(c) 2006-2007, Ext JS, LLC.
34519 * Originally Released Under LGPL - original licence link has changed is not relivant.
34522 * <script type="text/javascript">
34526 * @class Roo.bootstrap.layout.Basic
34527 * @extends Roo.util.Observable
34528 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
34529 * and does not have a titlebar, tabs or any other features. All it does is size and position
34530 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
34531 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
34532 * @cfg {string} region the region that it inhabits..
34533 * @cfg {bool} skipConfig skip config?
34537 Roo.bootstrap.layout.Basic = function(config){
34539 this.mgr = config.mgr;
34541 this.position = config.region;
34543 var skipConfig = config.skipConfig;
34547 * @scope Roo.BasicLayoutRegion
34551 * @event beforeremove
34552 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
34553 * @param {Roo.LayoutRegion} this
34554 * @param {Roo.ContentPanel} panel The panel
34555 * @param {Object} e The cancel event object
34557 "beforeremove" : true,
34559 * @event invalidated
34560 * Fires when the layout for this region is changed.
34561 * @param {Roo.LayoutRegion} this
34563 "invalidated" : true,
34565 * @event visibilitychange
34566 * Fires when this region is shown or hidden
34567 * @param {Roo.LayoutRegion} this
34568 * @param {Boolean} visibility true or false
34570 "visibilitychange" : true,
34572 * @event paneladded
34573 * Fires when a panel is added.
34574 * @param {Roo.LayoutRegion} this
34575 * @param {Roo.ContentPanel} panel The panel
34577 "paneladded" : true,
34579 * @event panelremoved
34580 * Fires when a panel is removed.
34581 * @param {Roo.LayoutRegion} this
34582 * @param {Roo.ContentPanel} panel The panel
34584 "panelremoved" : true,
34586 * @event beforecollapse
34587 * Fires when this region before collapse.
34588 * @param {Roo.LayoutRegion} this
34590 "beforecollapse" : true,
34593 * Fires when this region is collapsed.
34594 * @param {Roo.LayoutRegion} this
34596 "collapsed" : true,
34599 * Fires when this region is expanded.
34600 * @param {Roo.LayoutRegion} this
34605 * Fires when this region is slid into view.
34606 * @param {Roo.LayoutRegion} this
34608 "slideshow" : true,
34611 * Fires when this region slides out of view.
34612 * @param {Roo.LayoutRegion} this
34614 "slidehide" : true,
34616 * @event panelactivated
34617 * Fires when a panel is activated.
34618 * @param {Roo.LayoutRegion} this
34619 * @param {Roo.ContentPanel} panel The activated panel
34621 "panelactivated" : true,
34624 * Fires when the user resizes this region.
34625 * @param {Roo.LayoutRegion} this
34626 * @param {Number} newSize The new size (width for east/west, height for north/south)
34630 /** A collection of panels in this region. @type Roo.util.MixedCollection */
34631 this.panels = new Roo.util.MixedCollection();
34632 this.panels.getKey = this.getPanelId.createDelegate(this);
34634 this.activePanel = null;
34635 // ensure listeners are added...
34637 if (config.listeners || config.events) {
34638 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
34639 listeners : config.listeners || {},
34640 events : config.events || {}
34644 if(skipConfig !== true){
34645 this.applyConfig(config);
34649 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
34651 getPanelId : function(p){
34655 applyConfig : function(config){
34656 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
34657 this.config = config;
34662 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
34663 * the width, for horizontal (north, south) the height.
34664 * @param {Number} newSize The new width or height
34666 resizeTo : function(newSize){
34667 var el = this.el ? this.el :
34668 (this.activePanel ? this.activePanel.getEl() : null);
34670 switch(this.position){
34673 el.setWidth(newSize);
34674 this.fireEvent("resized", this, newSize);
34678 el.setHeight(newSize);
34679 this.fireEvent("resized", this, newSize);
34685 getBox : function(){
34686 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
34689 getMargins : function(){
34690 return this.margins;
34693 updateBox : function(box){
34695 var el = this.activePanel.getEl();
34696 el.dom.style.left = box.x + "px";
34697 el.dom.style.top = box.y + "px";
34698 this.activePanel.setSize(box.width, box.height);
34702 * Returns the container element for this region.
34703 * @return {Roo.Element}
34705 getEl : function(){
34706 return this.activePanel;
34710 * Returns true if this region is currently visible.
34711 * @return {Boolean}
34713 isVisible : function(){
34714 return this.activePanel ? true : false;
34717 setActivePanel : function(panel){
34718 panel = this.getPanel(panel);
34719 if(this.activePanel && this.activePanel != panel){
34720 this.activePanel.setActiveState(false);
34721 this.activePanel.getEl().setLeftTop(-10000,-10000);
34723 this.activePanel = panel;
34724 panel.setActiveState(true);
34726 panel.setSize(this.box.width, this.box.height);
34728 this.fireEvent("panelactivated", this, panel);
34729 this.fireEvent("invalidated");
34733 * Show the specified panel.
34734 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
34735 * @return {Roo.ContentPanel} The shown panel or null
34737 showPanel : function(panel){
34738 panel = this.getPanel(panel);
34740 this.setActivePanel(panel);
34746 * Get the active panel for this region.
34747 * @return {Roo.ContentPanel} The active panel or null
34749 getActivePanel : function(){
34750 return this.activePanel;
34754 * Add the passed ContentPanel(s)
34755 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34756 * @return {Roo.ContentPanel} The panel added (if only one was added)
34758 add : function(panel){
34759 if(arguments.length > 1){
34760 for(var i = 0, len = arguments.length; i < len; i++) {
34761 this.add(arguments[i]);
34765 if(this.hasPanel(panel)){
34766 this.showPanel(panel);
34769 var el = panel.getEl();
34770 if(el.dom.parentNode != this.mgr.el.dom){
34771 this.mgr.el.dom.appendChild(el.dom);
34773 if(panel.setRegion){
34774 panel.setRegion(this);
34776 this.panels.add(panel);
34777 el.setStyle("position", "absolute");
34778 if(!panel.background){
34779 this.setActivePanel(panel);
34780 if(this.config.initialSize && this.panels.getCount()==1){
34781 this.resizeTo(this.config.initialSize);
34784 this.fireEvent("paneladded", this, panel);
34789 * Returns true if the panel is in this region.
34790 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34791 * @return {Boolean}
34793 hasPanel : function(panel){
34794 if(typeof panel == "object"){ // must be panel obj
34795 panel = panel.getId();
34797 return this.getPanel(panel) ? true : false;
34801 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34802 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34803 * @param {Boolean} preservePanel Overrides the config preservePanel option
34804 * @return {Roo.ContentPanel} The panel that was removed
34806 remove : function(panel, preservePanel){
34807 panel = this.getPanel(panel);
34812 this.fireEvent("beforeremove", this, panel, e);
34813 if(e.cancel === true){
34816 var panelId = panel.getId();
34817 this.panels.removeKey(panelId);
34822 * Returns the panel specified or null if it's not in this region.
34823 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34824 * @return {Roo.ContentPanel}
34826 getPanel : function(id){
34827 if(typeof id == "object"){ // must be panel obj
34830 return this.panels.get(id);
34834 * Returns this regions position (north/south/east/west/center).
34837 getPosition: function(){
34838 return this.position;
34842 * Ext JS Library 1.1.1
34843 * Copyright(c) 2006-2007, Ext JS, LLC.
34845 * Originally Released Under LGPL - original licence link has changed is not relivant.
34848 * <script type="text/javascript">
34852 * @class Roo.bootstrap.layout.Region
34853 * @extends Roo.bootstrap.layout.Basic
34854 * This class represents a region in a layout manager.
34856 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
34857 * @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})
34858 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
34859 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
34860 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
34861 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
34862 * @cfg {String} title The title for the region (overrides panel titles)
34863 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
34864 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
34865 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
34866 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
34867 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
34868 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
34869 * the space available, similar to FireFox 1.5 tabs (defaults to false)
34870 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
34871 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
34872 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
34874 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
34875 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
34876 * @cfg {Boolean} disableTabTips True to disable tab tooltips
34877 * @cfg {Number} width For East/West panels
34878 * @cfg {Number} height For North/South panels
34879 * @cfg {Boolean} split To show the splitter
34880 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
34882 * @cfg {string} cls Extra CSS classes to add to region
34884 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
34885 * @cfg {string} region the region that it inhabits..
34888 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
34889 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
34891 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
34892 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
34893 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
34895 Roo.bootstrap.layout.Region = function(config)
34897 this.applyConfig(config);
34899 var mgr = config.mgr;
34900 var pos = config.region;
34901 config.skipConfig = true;
34902 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
34905 this.onRender(mgr.el);
34908 this.visible = true;
34909 this.collapsed = false;
34910 this.unrendered_panels = [];
34913 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
34915 position: '', // set by wrapper (eg. north/south etc..)
34916 unrendered_panels : null, // unrendered panels.
34917 createBody : function(){
34918 /** This region's body element
34919 * @type Roo.Element */
34920 this.bodyEl = this.el.createChild({
34922 cls: "roo-layout-panel-body tab-content" // bootstrap added...
34926 onRender: function(ctr, pos)
34928 var dh = Roo.DomHelper;
34929 /** This region's container element
34930 * @type Roo.Element */
34931 this.el = dh.append(ctr.dom, {
34933 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
34935 /** This region's title element
34936 * @type Roo.Element */
34938 this.titleEl = dh.append(this.el.dom,
34941 unselectable: "on",
34942 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
34944 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
34945 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
34948 this.titleEl.enableDisplayMode();
34949 /** This region's title text element
34950 * @type HTMLElement */
34951 this.titleTextEl = this.titleEl.dom.firstChild;
34952 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
34954 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
34955 this.closeBtn.enableDisplayMode();
34956 this.closeBtn.on("click", this.closeClicked, this);
34957 this.closeBtn.hide();
34959 this.createBody(this.config);
34960 if(this.config.hideWhenEmpty){
34962 this.on("paneladded", this.validateVisibility, this);
34963 this.on("panelremoved", this.validateVisibility, this);
34965 if(this.autoScroll){
34966 this.bodyEl.setStyle("overflow", "auto");
34968 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
34970 //if(c.titlebar !== false){
34971 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
34972 this.titleEl.hide();
34974 this.titleEl.show();
34975 if(this.config.title){
34976 this.titleTextEl.innerHTML = this.config.title;
34980 if(this.config.collapsed){
34981 this.collapse(true);
34983 if(this.config.hidden){
34987 if (this.unrendered_panels && this.unrendered_panels.length) {
34988 for (var i =0;i< this.unrendered_panels.length; i++) {
34989 this.add(this.unrendered_panels[i]);
34991 this.unrendered_panels = null;
34997 applyConfig : function(c)
35000 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35001 var dh = Roo.DomHelper;
35002 if(c.titlebar !== false){
35003 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35004 this.collapseBtn.on("click", this.collapse, this);
35005 this.collapseBtn.enableDisplayMode();
35007 if(c.showPin === true || this.showPin){
35008 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35009 this.stickBtn.enableDisplayMode();
35010 this.stickBtn.on("click", this.expand, this);
35011 this.stickBtn.hide();
35016 /** This region's collapsed element
35017 * @type Roo.Element */
35020 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35021 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35024 if(c.floatable !== false){
35025 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35026 this.collapsedEl.on("click", this.collapseClick, this);
35029 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35030 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35031 id: "message", unselectable: "on", style:{"float":"left"}});
35032 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35034 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35035 this.expandBtn.on("click", this.expand, this);
35039 if(this.collapseBtn){
35040 this.collapseBtn.setVisible(c.collapsible == true);
35043 this.cmargins = c.cmargins || this.cmargins ||
35044 (this.position == "west" || this.position == "east" ?
35045 {top: 0, left: 2, right:2, bottom: 0} :
35046 {top: 2, left: 0, right:0, bottom: 2});
35048 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35051 this.bottomTabs = c.tabPosition != "top";
35053 this.autoScroll = c.autoScroll || false;
35058 this.duration = c.duration || .30;
35059 this.slideDuration = c.slideDuration || .45;
35064 * Returns true if this region is currently visible.
35065 * @return {Boolean}
35067 isVisible : function(){
35068 return this.visible;
35072 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35073 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
35075 //setCollapsedTitle : function(title){
35076 // title = title || " ";
35077 // if(this.collapsedTitleTextEl){
35078 // this.collapsedTitleTextEl.innerHTML = title;
35082 getBox : function(){
35084 // if(!this.collapsed){
35085 b = this.el.getBox(false, true);
35087 // b = this.collapsedEl.getBox(false, true);
35092 getMargins : function(){
35093 return this.margins;
35094 //return this.collapsed ? this.cmargins : this.margins;
35097 highlight : function(){
35098 this.el.addClass("x-layout-panel-dragover");
35101 unhighlight : function(){
35102 this.el.removeClass("x-layout-panel-dragover");
35105 updateBox : function(box)
35107 if (!this.bodyEl) {
35108 return; // not rendered yet..
35112 if(!this.collapsed){
35113 this.el.dom.style.left = box.x + "px";
35114 this.el.dom.style.top = box.y + "px";
35115 this.updateBody(box.width, box.height);
35117 this.collapsedEl.dom.style.left = box.x + "px";
35118 this.collapsedEl.dom.style.top = box.y + "px";
35119 this.collapsedEl.setSize(box.width, box.height);
35122 this.tabs.autoSizeTabs();
35126 updateBody : function(w, h)
35129 this.el.setWidth(w);
35130 w -= this.el.getBorderWidth("rl");
35131 if(this.config.adjustments){
35132 w += this.config.adjustments[0];
35135 if(h !== null && h > 0){
35136 this.el.setHeight(h);
35137 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35138 h -= this.el.getBorderWidth("tb");
35139 if(this.config.adjustments){
35140 h += this.config.adjustments[1];
35142 this.bodyEl.setHeight(h);
35144 h = this.tabs.syncHeight(h);
35147 if(this.panelSize){
35148 w = w !== null ? w : this.panelSize.width;
35149 h = h !== null ? h : this.panelSize.height;
35151 if(this.activePanel){
35152 var el = this.activePanel.getEl();
35153 w = w !== null ? w : el.getWidth();
35154 h = h !== null ? h : el.getHeight();
35155 this.panelSize = {width: w, height: h};
35156 this.activePanel.setSize(w, h);
35158 if(Roo.isIE && this.tabs){
35159 this.tabs.el.repaint();
35164 * Returns the container element for this region.
35165 * @return {Roo.Element}
35167 getEl : function(){
35172 * Hides this region.
35175 //if(!this.collapsed){
35176 this.el.dom.style.left = "-2000px";
35179 // this.collapsedEl.dom.style.left = "-2000px";
35180 // this.collapsedEl.hide();
35182 this.visible = false;
35183 this.fireEvent("visibilitychange", this, false);
35187 * Shows this region if it was previously hidden.
35190 //if(!this.collapsed){
35193 // this.collapsedEl.show();
35195 this.visible = true;
35196 this.fireEvent("visibilitychange", this, true);
35199 closeClicked : function(){
35200 if(this.activePanel){
35201 this.remove(this.activePanel);
35205 collapseClick : function(e){
35207 e.stopPropagation();
35210 e.stopPropagation();
35216 * Collapses this region.
35217 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35220 collapse : function(skipAnim, skipCheck = false){
35221 if(this.collapsed) {
35225 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35227 this.collapsed = true;
35229 this.split.el.hide();
35231 if(this.config.animate && skipAnim !== true){
35232 this.fireEvent("invalidated", this);
35233 this.animateCollapse();
35235 this.el.setLocation(-20000,-20000);
35237 this.collapsedEl.show();
35238 this.fireEvent("collapsed", this);
35239 this.fireEvent("invalidated", this);
35245 animateCollapse : function(){
35250 * Expands this region if it was previously collapsed.
35251 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35252 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35255 expand : function(e, skipAnim){
35257 e.stopPropagation();
35259 if(!this.collapsed || this.el.hasActiveFx()) {
35263 this.afterSlideIn();
35266 this.collapsed = false;
35267 if(this.config.animate && skipAnim !== true){
35268 this.animateExpand();
35272 this.split.el.show();
35274 this.collapsedEl.setLocation(-2000,-2000);
35275 this.collapsedEl.hide();
35276 this.fireEvent("invalidated", this);
35277 this.fireEvent("expanded", this);
35281 animateExpand : function(){
35285 initTabs : function()
35287 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35289 var ts = new Roo.bootstrap.panel.Tabs({
35290 el: this.bodyEl.dom,
35291 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35292 disableTooltips: this.config.disableTabTips,
35293 toolbar : this.config.toolbar
35296 if(this.config.hideTabs){
35297 ts.stripWrap.setDisplayed(false);
35300 ts.resizeTabs = this.config.resizeTabs === true;
35301 ts.minTabWidth = this.config.minTabWidth || 40;
35302 ts.maxTabWidth = this.config.maxTabWidth || 250;
35303 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35304 ts.monitorResize = false;
35305 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35306 ts.bodyEl.addClass('roo-layout-tabs-body');
35307 this.panels.each(this.initPanelAsTab, this);
35310 initPanelAsTab : function(panel){
35311 var ti = this.tabs.addTab(
35315 this.config.closeOnTab && panel.isClosable(),
35318 if(panel.tabTip !== undefined){
35319 ti.setTooltip(panel.tabTip);
35321 ti.on("activate", function(){
35322 this.setActivePanel(panel);
35325 if(this.config.closeOnTab){
35326 ti.on("beforeclose", function(t, e){
35328 this.remove(panel);
35332 panel.tabItem = ti;
35337 updatePanelTitle : function(panel, title)
35339 if(this.activePanel == panel){
35340 this.updateTitle(title);
35343 var ti = this.tabs.getTab(panel.getEl().id);
35345 if(panel.tabTip !== undefined){
35346 ti.setTooltip(panel.tabTip);
35351 updateTitle : function(title){
35352 if(this.titleTextEl && !this.config.title){
35353 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
35357 setActivePanel : function(panel)
35359 panel = this.getPanel(panel);
35360 if(this.activePanel && this.activePanel != panel){
35361 this.activePanel.setActiveState(false);
35363 this.activePanel = panel;
35364 panel.setActiveState(true);
35365 if(this.panelSize){
35366 panel.setSize(this.panelSize.width, this.panelSize.height);
35369 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35371 this.updateTitle(panel.getTitle());
35373 this.fireEvent("invalidated", this);
35375 this.fireEvent("panelactivated", this, panel);
35379 * Shows the specified panel.
35380 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35381 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35383 showPanel : function(panel)
35385 panel = this.getPanel(panel);
35388 var tab = this.tabs.getTab(panel.getEl().id);
35389 if(tab.isHidden()){
35390 this.tabs.unhideTab(tab.id);
35394 this.setActivePanel(panel);
35401 * Get the active panel for this region.
35402 * @return {Roo.ContentPanel} The active panel or null
35404 getActivePanel : function(){
35405 return this.activePanel;
35408 validateVisibility : function(){
35409 if(this.panels.getCount() < 1){
35410 this.updateTitle(" ");
35411 this.closeBtn.hide();
35414 if(!this.isVisible()){
35421 * Adds the passed ContentPanel(s) to this region.
35422 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35423 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
35425 add : function(panel)
35427 if(arguments.length > 1){
35428 for(var i = 0, len = arguments.length; i < len; i++) {
35429 this.add(arguments[i]);
35434 // if we have not been rendered yet, then we can not really do much of this..
35435 if (!this.bodyEl) {
35436 this.unrendered_panels.push(panel);
35443 if(this.hasPanel(panel)){
35444 this.showPanel(panel);
35447 panel.setRegion(this);
35448 this.panels.add(panel);
35449 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
35450 // sinle panel - no tab...?? would it not be better to render it with the tabs,
35451 // and hide them... ???
35452 this.bodyEl.dom.appendChild(panel.getEl().dom);
35453 if(panel.background !== true){
35454 this.setActivePanel(panel);
35456 this.fireEvent("paneladded", this, panel);
35463 this.initPanelAsTab(panel);
35467 if(panel.background !== true){
35468 this.tabs.activate(panel.getEl().id);
35470 this.fireEvent("paneladded", this, panel);
35475 * Hides the tab for the specified panel.
35476 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35478 hidePanel : function(panel){
35479 if(this.tabs && (panel = this.getPanel(panel))){
35480 this.tabs.hideTab(panel.getEl().id);
35485 * Unhides the tab for a previously hidden panel.
35486 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35488 unhidePanel : function(panel){
35489 if(this.tabs && (panel = this.getPanel(panel))){
35490 this.tabs.unhideTab(panel.getEl().id);
35494 clearPanels : function(){
35495 while(this.panels.getCount() > 0){
35496 this.remove(this.panels.first());
35501 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35502 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35503 * @param {Boolean} preservePanel Overrides the config preservePanel option
35504 * @return {Roo.ContentPanel} The panel that was removed
35506 remove : function(panel, preservePanel)
35508 panel = this.getPanel(panel);
35513 this.fireEvent("beforeremove", this, panel, e);
35514 if(e.cancel === true){
35517 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
35518 var panelId = panel.getId();
35519 this.panels.removeKey(panelId);
35521 document.body.appendChild(panel.getEl().dom);
35524 this.tabs.removeTab(panel.getEl().id);
35525 }else if (!preservePanel){
35526 this.bodyEl.dom.removeChild(panel.getEl().dom);
35528 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
35529 var p = this.panels.first();
35530 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
35531 tempEl.appendChild(p.getEl().dom);
35532 this.bodyEl.update("");
35533 this.bodyEl.dom.appendChild(p.getEl().dom);
35535 this.updateTitle(p.getTitle());
35537 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
35538 this.setActivePanel(p);
35540 panel.setRegion(null);
35541 if(this.activePanel == panel){
35542 this.activePanel = null;
35544 if(this.config.autoDestroy !== false && preservePanel !== true){
35545 try{panel.destroy();}catch(e){}
35547 this.fireEvent("panelremoved", this, panel);
35552 * Returns the TabPanel component used by this region
35553 * @return {Roo.TabPanel}
35555 getTabs : function(){
35559 createTool : function(parentEl, className){
35560 var btn = Roo.DomHelper.append(parentEl, {
35562 cls: "x-layout-tools-button",
35565 cls: "roo-layout-tools-button-inner " + className,
35569 btn.addClassOnOver("roo-layout-tools-button-over");
35574 * Ext JS Library 1.1.1
35575 * Copyright(c) 2006-2007, Ext JS, LLC.
35577 * Originally Released Under LGPL - original licence link has changed is not relivant.
35580 * <script type="text/javascript">
35586 * @class Roo.SplitLayoutRegion
35587 * @extends Roo.LayoutRegion
35588 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
35590 Roo.bootstrap.layout.Split = function(config){
35591 this.cursor = config.cursor;
35592 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
35595 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
35597 splitTip : "Drag to resize.",
35598 collapsibleSplitTip : "Drag to resize. Double click to hide.",
35599 useSplitTips : false,
35601 applyConfig : function(config){
35602 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
35605 onRender : function(ctr,pos) {
35607 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
35608 if(!this.config.split){
35613 var splitEl = Roo.DomHelper.append(ctr.dom, {
35615 id: this.el.id + "-split",
35616 cls: "roo-layout-split roo-layout-split-"+this.position,
35619 /** The SplitBar for this region
35620 * @type Roo.SplitBar */
35621 // does not exist yet...
35622 Roo.log([this.position, this.orientation]);
35624 this.split = new Roo.bootstrap.SplitBar({
35625 dragElement : splitEl,
35626 resizingElement: this.el,
35627 orientation : this.orientation
35630 this.split.on("moved", this.onSplitMove, this);
35631 this.split.useShim = this.config.useShim === true;
35632 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
35633 if(this.useSplitTips){
35634 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
35636 //if(config.collapsible){
35637 // this.split.el.on("dblclick", this.collapse, this);
35640 if(typeof this.config.minSize != "undefined"){
35641 this.split.minSize = this.config.minSize;
35643 if(typeof this.config.maxSize != "undefined"){
35644 this.split.maxSize = this.config.maxSize;
35646 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
35647 this.hideSplitter();
35652 getHMaxSize : function(){
35653 var cmax = this.config.maxSize || 10000;
35654 var center = this.mgr.getRegion("center");
35655 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
35658 getVMaxSize : function(){
35659 var cmax = this.config.maxSize || 10000;
35660 var center = this.mgr.getRegion("center");
35661 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
35664 onSplitMove : function(split, newSize){
35665 this.fireEvent("resized", this, newSize);
35669 * Returns the {@link Roo.SplitBar} for this region.
35670 * @return {Roo.SplitBar}
35672 getSplitBar : function(){
35677 this.hideSplitter();
35678 Roo.bootstrap.layout.Split.superclass.hide.call(this);
35681 hideSplitter : function(){
35683 this.split.el.setLocation(-2000,-2000);
35684 this.split.el.hide();
35690 this.split.el.show();
35692 Roo.bootstrap.layout.Split.superclass.show.call(this);
35695 beforeSlide: function(){
35696 if(Roo.isGecko){// firefox overflow auto bug workaround
35697 this.bodyEl.clip();
35699 this.tabs.bodyEl.clip();
35701 if(this.activePanel){
35702 this.activePanel.getEl().clip();
35704 if(this.activePanel.beforeSlide){
35705 this.activePanel.beforeSlide();
35711 afterSlide : function(){
35712 if(Roo.isGecko){// firefox overflow auto bug workaround
35713 this.bodyEl.unclip();
35715 this.tabs.bodyEl.unclip();
35717 if(this.activePanel){
35718 this.activePanel.getEl().unclip();
35719 if(this.activePanel.afterSlide){
35720 this.activePanel.afterSlide();
35726 initAutoHide : function(){
35727 if(this.autoHide !== false){
35728 if(!this.autoHideHd){
35729 var st = new Roo.util.DelayedTask(this.slideIn, this);
35730 this.autoHideHd = {
35731 "mouseout": function(e){
35732 if(!e.within(this.el, true)){
35736 "mouseover" : function(e){
35742 this.el.on(this.autoHideHd);
35746 clearAutoHide : function(){
35747 if(this.autoHide !== false){
35748 this.el.un("mouseout", this.autoHideHd.mouseout);
35749 this.el.un("mouseover", this.autoHideHd.mouseover);
35753 clearMonitor : function(){
35754 Roo.get(document).un("click", this.slideInIf, this);
35757 // these names are backwards but not changed for compat
35758 slideOut : function(){
35759 if(this.isSlid || this.el.hasActiveFx()){
35762 this.isSlid = true;
35763 if(this.collapseBtn){
35764 this.collapseBtn.hide();
35766 this.closeBtnState = this.closeBtn.getStyle('display');
35767 this.closeBtn.hide();
35769 this.stickBtn.show();
35772 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
35773 this.beforeSlide();
35774 this.el.setStyle("z-index", 10001);
35775 this.el.slideIn(this.getSlideAnchor(), {
35776 callback: function(){
35778 this.initAutoHide();
35779 Roo.get(document).on("click", this.slideInIf, this);
35780 this.fireEvent("slideshow", this);
35787 afterSlideIn : function(){
35788 this.clearAutoHide();
35789 this.isSlid = false;
35790 this.clearMonitor();
35791 this.el.setStyle("z-index", "");
35792 if(this.collapseBtn){
35793 this.collapseBtn.show();
35795 this.closeBtn.setStyle('display', this.closeBtnState);
35797 this.stickBtn.hide();
35799 this.fireEvent("slidehide", this);
35802 slideIn : function(cb){
35803 if(!this.isSlid || this.el.hasActiveFx()){
35807 this.isSlid = false;
35808 this.beforeSlide();
35809 this.el.slideOut(this.getSlideAnchor(), {
35810 callback: function(){
35811 this.el.setLeftTop(-10000, -10000);
35813 this.afterSlideIn();
35821 slideInIf : function(e){
35822 if(!e.within(this.el)){
35827 animateCollapse : function(){
35828 this.beforeSlide();
35829 this.el.setStyle("z-index", 20000);
35830 var anchor = this.getSlideAnchor();
35831 this.el.slideOut(anchor, {
35832 callback : function(){
35833 this.el.setStyle("z-index", "");
35834 this.collapsedEl.slideIn(anchor, {duration:.3});
35836 this.el.setLocation(-10000,-10000);
35838 this.fireEvent("collapsed", this);
35845 animateExpand : function(){
35846 this.beforeSlide();
35847 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
35848 this.el.setStyle("z-index", 20000);
35849 this.collapsedEl.hide({
35852 this.el.slideIn(this.getSlideAnchor(), {
35853 callback : function(){
35854 this.el.setStyle("z-index", "");
35857 this.split.el.show();
35859 this.fireEvent("invalidated", this);
35860 this.fireEvent("expanded", this);
35888 getAnchor : function(){
35889 return this.anchors[this.position];
35892 getCollapseAnchor : function(){
35893 return this.canchors[this.position];
35896 getSlideAnchor : function(){
35897 return this.sanchors[this.position];
35900 getAlignAdj : function(){
35901 var cm = this.cmargins;
35902 switch(this.position){
35918 getExpandAdj : function(){
35919 var c = this.collapsedEl, cm = this.cmargins;
35920 switch(this.position){
35922 return [-(cm.right+c.getWidth()+cm.left), 0];
35925 return [cm.right+c.getWidth()+cm.left, 0];
35928 return [0, -(cm.top+cm.bottom+c.getHeight())];
35931 return [0, cm.top+cm.bottom+c.getHeight()];
35937 * Ext JS Library 1.1.1
35938 * Copyright(c) 2006-2007, Ext JS, LLC.
35940 * Originally Released Under LGPL - original licence link has changed is not relivant.
35943 * <script type="text/javascript">
35946 * These classes are private internal classes
35948 Roo.bootstrap.layout.Center = function(config){
35949 config.region = "center";
35950 Roo.bootstrap.layout.Region.call(this, config);
35951 this.visible = true;
35952 this.minWidth = config.minWidth || 20;
35953 this.minHeight = config.minHeight || 20;
35956 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
35958 // center panel can't be hidden
35962 // center panel can't be hidden
35965 getMinWidth: function(){
35966 return this.minWidth;
35969 getMinHeight: function(){
35970 return this.minHeight;
35983 Roo.bootstrap.layout.North = function(config)
35985 config.region = 'north';
35986 config.cursor = 'n-resize';
35988 Roo.bootstrap.layout.Split.call(this, config);
35992 this.split.placement = Roo.bootstrap.SplitBar.TOP;
35993 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
35994 this.split.el.addClass("roo-layout-split-v");
35996 var size = config.initialSize || config.height;
35997 if(typeof size != "undefined"){
35998 this.el.setHeight(size);
36001 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36003 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36007 getBox : function(){
36008 if(this.collapsed){
36009 return this.collapsedEl.getBox();
36011 var box = this.el.getBox();
36013 box.height += this.split.el.getHeight();
36018 updateBox : function(box){
36019 if(this.split && !this.collapsed){
36020 box.height -= this.split.el.getHeight();
36021 this.split.el.setLeft(box.x);
36022 this.split.el.setTop(box.y+box.height);
36023 this.split.el.setWidth(box.width);
36025 if(this.collapsed){
36026 this.updateBody(box.width, null);
36028 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36036 Roo.bootstrap.layout.South = function(config){
36037 config.region = 'south';
36038 config.cursor = 's-resize';
36039 Roo.bootstrap.layout.Split.call(this, config);
36041 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36042 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36043 this.split.el.addClass("roo-layout-split-v");
36045 var size = config.initialSize || config.height;
36046 if(typeof size != "undefined"){
36047 this.el.setHeight(size);
36051 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36052 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36053 getBox : function(){
36054 if(this.collapsed){
36055 return this.collapsedEl.getBox();
36057 var box = this.el.getBox();
36059 var sh = this.split.el.getHeight();
36066 updateBox : function(box){
36067 if(this.split && !this.collapsed){
36068 var sh = this.split.el.getHeight();
36071 this.split.el.setLeft(box.x);
36072 this.split.el.setTop(box.y-sh);
36073 this.split.el.setWidth(box.width);
36075 if(this.collapsed){
36076 this.updateBody(box.width, null);
36078 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36082 Roo.bootstrap.layout.East = function(config){
36083 config.region = "east";
36084 config.cursor = "e-resize";
36085 Roo.bootstrap.layout.Split.call(this, config);
36087 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36088 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36089 this.split.el.addClass("roo-layout-split-h");
36091 var size = config.initialSize || config.width;
36092 if(typeof size != "undefined"){
36093 this.el.setWidth(size);
36096 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36097 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36098 getBox : function(){
36099 if(this.collapsed){
36100 return this.collapsedEl.getBox();
36102 var box = this.el.getBox();
36104 var sw = this.split.el.getWidth();
36111 updateBox : function(box){
36112 if(this.split && !this.collapsed){
36113 var sw = this.split.el.getWidth();
36115 this.split.el.setLeft(box.x);
36116 this.split.el.setTop(box.y);
36117 this.split.el.setHeight(box.height);
36120 if(this.collapsed){
36121 this.updateBody(null, box.height);
36123 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36127 Roo.bootstrap.layout.West = function(config){
36128 config.region = "west";
36129 config.cursor = "w-resize";
36131 Roo.bootstrap.layout.Split.call(this, config);
36133 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36134 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36135 this.split.el.addClass("roo-layout-split-h");
36139 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36140 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36142 onRender: function(ctr, pos)
36144 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36145 var size = this.config.initialSize || this.config.width;
36146 if(typeof size != "undefined"){
36147 this.el.setWidth(size);
36151 getBox : function(){
36152 if(this.collapsed){
36153 return this.collapsedEl.getBox();
36155 var box = this.el.getBox();
36157 box.width += this.split.el.getWidth();
36162 updateBox : function(box){
36163 if(this.split && !this.collapsed){
36164 var sw = this.split.el.getWidth();
36166 this.split.el.setLeft(box.x+box.width);
36167 this.split.el.setTop(box.y);
36168 this.split.el.setHeight(box.height);
36170 if(this.collapsed){
36171 this.updateBody(null, box.height);
36173 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36176 Roo.namespace("Roo.bootstrap.panel");/*
36178 * Ext JS Library 1.1.1
36179 * Copyright(c) 2006-2007, Ext JS, LLC.
36181 * Originally Released Under LGPL - original licence link has changed is not relivant.
36184 * <script type="text/javascript">
36187 * @class Roo.ContentPanel
36188 * @extends Roo.util.Observable
36189 * A basic ContentPanel element.
36190 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
36191 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
36192 * @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
36193 * @cfg {Boolean} closable True if the panel can be closed/removed
36194 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
36195 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36196 * @cfg {Toolbar} toolbar A toolbar for this panel
36197 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
36198 * @cfg {String} title The title for this panel
36199 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36200 * @cfg {String} url Calls {@link #setUrl} with this value
36201 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36202 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
36203 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
36204 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
36205 * @cfg {Boolean} badges render the badges
36208 * Create a new ContentPanel.
36209 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36210 * @param {String/Object} config A string to set only the title or a config object
36211 * @param {String} content (optional) Set the HTML content for this panel
36212 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36214 Roo.bootstrap.panel.Content = function( config){
36216 this.tpl = config.tpl || false;
36218 var el = config.el;
36219 var content = config.content;
36221 if(config.autoCreate){ // xtype is available if this is called from factory
36224 this.el = Roo.get(el);
36225 if(!this.el && config && config.autoCreate){
36226 if(typeof config.autoCreate == "object"){
36227 if(!config.autoCreate.id){
36228 config.autoCreate.id = config.id||el;
36230 this.el = Roo.DomHelper.append(document.body,
36231 config.autoCreate, true);
36233 var elcfg = { tag: "div",
36234 cls: "roo-layout-inactive-content",
36238 elcfg.html = config.html;
36242 this.el = Roo.DomHelper.append(document.body, elcfg , true);
36245 this.closable = false;
36246 this.loaded = false;
36247 this.active = false;
36250 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36252 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36254 this.wrapEl = this.el; //this.el.wrap();
36256 if (config.toolbar.items) {
36257 ti = config.toolbar.items ;
36258 delete config.toolbar.items ;
36262 this.toolbar.render(this.wrapEl, 'before');
36263 for(var i =0;i < ti.length;i++) {
36264 // Roo.log(['add child', items[i]]);
36265 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36267 this.toolbar.items = nitems;
36268 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36269 delete config.toolbar;
36273 // xtype created footer. - not sure if will work as we normally have to render first..
36274 if (this.footer && !this.footer.el && this.footer.xtype) {
36275 if (!this.wrapEl) {
36276 this.wrapEl = this.el.wrap();
36279 this.footer.container = this.wrapEl.createChild();
36281 this.footer = Roo.factory(this.footer, Roo);
36286 if(typeof config == "string"){
36287 this.title = config;
36289 Roo.apply(this, config);
36293 this.resizeEl = Roo.get(this.resizeEl, true);
36295 this.resizeEl = this.el;
36297 // handle view.xtype
36305 * Fires when this panel is activated.
36306 * @param {Roo.ContentPanel} this
36310 * @event deactivate
36311 * Fires when this panel is activated.
36312 * @param {Roo.ContentPanel} this
36314 "deactivate" : true,
36318 * Fires when this panel is resized if fitToFrame is true.
36319 * @param {Roo.ContentPanel} this
36320 * @param {Number} width The width after any component adjustments
36321 * @param {Number} height The height after any component adjustments
36327 * Fires when this tab is created
36328 * @param {Roo.ContentPanel} this
36339 if(this.autoScroll){
36340 this.resizeEl.setStyle("overflow", "auto");
36342 // fix randome scrolling
36343 //this.el.on('scroll', function() {
36344 // Roo.log('fix random scolling');
36345 // this.scrollTo('top',0);
36348 content = content || this.content;
36350 this.setContent(content);
36352 if(config && config.url){
36353 this.setUrl(this.url, this.params, this.loadOnce);
36358 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36360 if (this.view && typeof(this.view.xtype) != 'undefined') {
36361 this.view.el = this.el.appendChild(document.createElement("div"));
36362 this.view = Roo.factory(this.view);
36363 this.view.render && this.view.render(false, '');
36367 this.fireEvent('render', this);
36370 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36374 setRegion : function(region){
36375 this.region = region;
36376 this.setActiveClass(region && !this.background);
36380 setActiveClass: function(state)
36383 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36384 this.el.setStyle('position','relative');
36386 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36387 this.el.setStyle('position', 'absolute');
36392 * Returns the toolbar for this Panel if one was configured.
36393 * @return {Roo.Toolbar}
36395 getToolbar : function(){
36396 return this.toolbar;
36399 setActiveState : function(active)
36401 this.active = active;
36402 this.setActiveClass(active);
36404 this.fireEvent("deactivate", this);
36406 this.fireEvent("activate", this);
36410 * Updates this panel's element
36411 * @param {String} content The new content
36412 * @param {Boolean} loadScripts (optional) true to look for and process scripts
36414 setContent : function(content, loadScripts){
36415 this.el.update(content, loadScripts);
36418 ignoreResize : function(w, h){
36419 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
36422 this.lastSize = {width: w, height: h};
36427 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
36428 * @return {Roo.UpdateManager} The UpdateManager
36430 getUpdateManager : function(){
36431 return this.el.getUpdateManager();
36434 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
36435 * @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:
36438 url: "your-url.php",
36439 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
36440 callback: yourFunction,
36441 scope: yourObject, //(optional scope)
36444 text: "Loading...",
36449 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
36450 * 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.
36451 * @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}
36452 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
36453 * @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.
36454 * @return {Roo.ContentPanel} this
36457 var um = this.el.getUpdateManager();
36458 um.update.apply(um, arguments);
36464 * 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.
36465 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
36466 * @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)
36467 * @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)
36468 * @return {Roo.UpdateManager} The UpdateManager
36470 setUrl : function(url, params, loadOnce){
36471 if(this.refreshDelegate){
36472 this.removeListener("activate", this.refreshDelegate);
36474 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
36475 this.on("activate", this.refreshDelegate);
36476 return this.el.getUpdateManager();
36479 _handleRefresh : function(url, params, loadOnce){
36480 if(!loadOnce || !this.loaded){
36481 var updater = this.el.getUpdateManager();
36482 updater.update(url, params, this._setLoaded.createDelegate(this));
36486 _setLoaded : function(){
36487 this.loaded = true;
36491 * Returns this panel's id
36494 getId : function(){
36499 * Returns this panel's element - used by regiosn to add.
36500 * @return {Roo.Element}
36502 getEl : function(){
36503 return this.wrapEl || this.el;
36508 adjustForComponents : function(width, height)
36510 //Roo.log('adjustForComponents ');
36511 if(this.resizeEl != this.el){
36512 width -= this.el.getFrameWidth('lr');
36513 height -= this.el.getFrameWidth('tb');
36516 var te = this.toolbar.getEl();
36517 te.setWidth(width);
36518 height -= te.getHeight();
36521 var te = this.footer.getEl();
36522 te.setWidth(width);
36523 height -= te.getHeight();
36527 if(this.adjustments){
36528 width += this.adjustments[0];
36529 height += this.adjustments[1];
36531 return {"width": width, "height": height};
36534 setSize : function(width, height){
36535 if(this.fitToFrame && !this.ignoreResize(width, height)){
36536 if(this.fitContainer && this.resizeEl != this.el){
36537 this.el.setSize(width, height);
36539 var size = this.adjustForComponents(width, height);
36540 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
36541 this.fireEvent('resize', this, size.width, size.height);
36546 * Returns this panel's title
36549 getTitle : function(){
36551 if (typeof(this.title) != 'object') {
36556 for (var k in this.title) {
36557 if (!this.title.hasOwnProperty(k)) {
36561 if (k.indexOf('-') >= 0) {
36562 var s = k.split('-');
36563 for (var i = 0; i<s.length; i++) {
36564 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
36567 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
36574 * Set this panel's title
36575 * @param {String} title
36577 setTitle : function(title){
36578 this.title = title;
36580 this.region.updatePanelTitle(this, title);
36585 * Returns true is this panel was configured to be closable
36586 * @return {Boolean}
36588 isClosable : function(){
36589 return this.closable;
36592 beforeSlide : function(){
36594 this.resizeEl.clip();
36597 afterSlide : function(){
36599 this.resizeEl.unclip();
36603 * Force a content refresh from the URL specified in the {@link #setUrl} method.
36604 * Will fail silently if the {@link #setUrl} method has not been called.
36605 * This does not activate the panel, just updates its content.
36607 refresh : function(){
36608 if(this.refreshDelegate){
36609 this.loaded = false;
36610 this.refreshDelegate();
36615 * Destroys this panel
36617 destroy : function(){
36618 this.el.removeAllListeners();
36619 var tempEl = document.createElement("span");
36620 tempEl.appendChild(this.el.dom);
36621 tempEl.innerHTML = "";
36627 * form - if the content panel contains a form - this is a reference to it.
36628 * @type {Roo.form.Form}
36632 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
36633 * This contains a reference to it.
36639 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
36649 * @param {Object} cfg Xtype definition of item to add.
36653 getChildContainer: function () {
36654 return this.getEl();
36659 var ret = new Roo.factory(cfg);
36664 if (cfg.xtype.match(/^Form$/)) {
36667 //if (this.footer) {
36668 // el = this.footer.container.insertSibling(false, 'before');
36670 el = this.el.createChild();
36673 this.form = new Roo.form.Form(cfg);
36676 if ( this.form.allItems.length) {
36677 this.form.render(el.dom);
36681 // should only have one of theses..
36682 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
36683 // views.. should not be just added - used named prop 'view''
36685 cfg.el = this.el.appendChild(document.createElement("div"));
36688 var ret = new Roo.factory(cfg);
36690 ret.render && ret.render(false, ''); // render blank..
36700 * @class Roo.bootstrap.panel.Grid
36701 * @extends Roo.bootstrap.panel.Content
36703 * Create a new GridPanel.
36704 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
36705 * @param {Object} config A the config object
36711 Roo.bootstrap.panel.Grid = function(config)
36715 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
36716 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
36718 config.el = this.wrapper;
36719 //this.el = this.wrapper;
36721 if (config.container) {
36722 // ctor'ed from a Border/panel.grid
36725 this.wrapper.setStyle("overflow", "hidden");
36726 this.wrapper.addClass('roo-grid-container');
36731 if(config.toolbar){
36732 var tool_el = this.wrapper.createChild();
36733 this.toolbar = Roo.factory(config.toolbar);
36735 if (config.toolbar.items) {
36736 ti = config.toolbar.items ;
36737 delete config.toolbar.items ;
36741 this.toolbar.render(tool_el);
36742 for(var i =0;i < ti.length;i++) {
36743 // Roo.log(['add child', items[i]]);
36744 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36746 this.toolbar.items = nitems;
36748 delete config.toolbar;
36751 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
36752 config.grid.scrollBody = true;;
36753 config.grid.monitorWindowResize = false; // turn off autosizing
36754 config.grid.autoHeight = false;
36755 config.grid.autoWidth = false;
36757 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
36759 if (config.background) {
36760 // render grid on panel activation (if panel background)
36761 this.on('activate', function(gp) {
36762 if (!gp.grid.rendered) {
36763 gp.grid.render(this.wrapper);
36764 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
36769 this.grid.render(this.wrapper);
36770 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
36773 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
36774 // ??? needed ??? config.el = this.wrapper;
36779 // xtype created footer. - not sure if will work as we normally have to render first..
36780 if (this.footer && !this.footer.el && this.footer.xtype) {
36782 var ctr = this.grid.getView().getFooterPanel(true);
36783 this.footer.dataSource = this.grid.dataSource;
36784 this.footer = Roo.factory(this.footer, Roo);
36785 this.footer.render(ctr);
36795 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
36796 getId : function(){
36797 return this.grid.id;
36801 * Returns the grid for this panel
36802 * @return {Roo.bootstrap.Table}
36804 getGrid : function(){
36808 setSize : function(width, height){
36809 if(!this.ignoreResize(width, height)){
36810 var grid = this.grid;
36811 var size = this.adjustForComponents(width, height);
36812 var gridel = grid.getGridEl();
36813 gridel.setSize(size.width, size.height);
36815 var thd = grid.getGridEl().select('thead',true).first();
36816 var tbd = grid.getGridEl().select('tbody', true).first();
36818 tbd.setSize(width, height - thd.getHeight());
36827 beforeSlide : function(){
36828 this.grid.getView().scroller.clip();
36831 afterSlide : function(){
36832 this.grid.getView().scroller.unclip();
36835 destroy : function(){
36836 this.grid.destroy();
36838 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
36843 * @class Roo.bootstrap.panel.Nest
36844 * @extends Roo.bootstrap.panel.Content
36846 * Create a new Panel, that can contain a layout.Border.
36849 * @param {Roo.BorderLayout} layout The layout for this panel
36850 * @param {String/Object} config A string to set only the title or a config object
36852 Roo.bootstrap.panel.Nest = function(config)
36854 // construct with only one argument..
36855 /* FIXME - implement nicer consturctors
36856 if (layout.layout) {
36858 layout = config.layout;
36859 delete config.layout;
36861 if (layout.xtype && !layout.getEl) {
36862 // then layout needs constructing..
36863 layout = Roo.factory(layout, Roo);
36867 config.el = config.layout.getEl();
36869 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
36871 config.layout.monitorWindowResize = false; // turn off autosizing
36872 this.layout = config.layout;
36873 this.layout.getEl().addClass("roo-layout-nested-layout");
36880 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
36882 setSize : function(width, height){
36883 if(!this.ignoreResize(width, height)){
36884 var size = this.adjustForComponents(width, height);
36885 var el = this.layout.getEl();
36886 if (size.height < 1) {
36887 el.setWidth(size.width);
36889 el.setSize(size.width, size.height);
36891 var touch = el.dom.offsetWidth;
36892 this.layout.layout();
36893 // ie requires a double layout on the first pass
36894 if(Roo.isIE && !this.initialized){
36895 this.initialized = true;
36896 this.layout.layout();
36901 // activate all subpanels if not currently active..
36903 setActiveState : function(active){
36904 this.active = active;
36905 this.setActiveClass(active);
36908 this.fireEvent("deactivate", this);
36912 this.fireEvent("activate", this);
36913 // not sure if this should happen before or after..
36914 if (!this.layout) {
36915 return; // should not happen..
36918 for (var r in this.layout.regions) {
36919 reg = this.layout.getRegion(r);
36920 if (reg.getActivePanel()) {
36921 //reg.showPanel(reg.getActivePanel()); // force it to activate..
36922 reg.setActivePanel(reg.getActivePanel());
36925 if (!reg.panels.length) {
36928 reg.showPanel(reg.getPanel(0));
36937 * Returns the nested BorderLayout for this panel
36938 * @return {Roo.BorderLayout}
36940 getLayout : function(){
36941 return this.layout;
36945 * Adds a xtype elements to the layout of the nested panel
36949 xtype : 'ContentPanel',
36956 xtype : 'NestedLayoutPanel',
36962 items : [ ... list of content panels or nested layout panels.. ]
36966 * @param {Object} cfg Xtype definition of item to add.
36968 addxtype : function(cfg) {
36969 return this.layout.addxtype(cfg);
36974 * Ext JS Library 1.1.1
36975 * Copyright(c) 2006-2007, Ext JS, LLC.
36977 * Originally Released Under LGPL - original licence link has changed is not relivant.
36980 * <script type="text/javascript">
36983 * @class Roo.TabPanel
36984 * @extends Roo.util.Observable
36985 * A lightweight tab container.
36989 // basic tabs 1, built from existing content
36990 var tabs = new Roo.TabPanel("tabs1");
36991 tabs.addTab("script", "View Script");
36992 tabs.addTab("markup", "View Markup");
36993 tabs.activate("script");
36995 // more advanced tabs, built from javascript
36996 var jtabs = new Roo.TabPanel("jtabs");
36997 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
36999 // set up the UpdateManager
37000 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37001 var updater = tab2.getUpdateManager();
37002 updater.setDefaultUrl("ajax1.htm");
37003 tab2.on('activate', updater.refresh, updater, true);
37005 // Use setUrl for Ajax loading
37006 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37007 tab3.setUrl("ajax2.htm", null, true);
37010 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37013 jtabs.activate("jtabs-1");
37016 * Create a new TabPanel.
37017 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37018 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37020 Roo.bootstrap.panel.Tabs = function(config){
37022 * The container element for this TabPanel.
37023 * @type Roo.Element
37025 this.el = Roo.get(config.el);
37028 if(typeof config == "boolean"){
37029 this.tabPosition = config ? "bottom" : "top";
37031 Roo.apply(this, config);
37035 if(this.tabPosition == "bottom"){
37036 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37037 this.el.addClass("roo-tabs-bottom");
37039 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37040 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37041 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37043 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37045 if(this.tabPosition != "bottom"){
37046 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37047 * @type Roo.Element
37049 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37050 this.el.addClass("roo-tabs-top");
37054 this.bodyEl.setStyle("position", "relative");
37056 this.active = null;
37057 this.activateDelegate = this.activate.createDelegate(this);
37062 * Fires when the active tab changes
37063 * @param {Roo.TabPanel} this
37064 * @param {Roo.TabPanelItem} activePanel The new active tab
37068 * @event beforetabchange
37069 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37070 * @param {Roo.TabPanel} this
37071 * @param {Object} e Set cancel to true on this object to cancel the tab change
37072 * @param {Roo.TabPanelItem} tab The tab being changed to
37074 "beforetabchange" : true
37077 Roo.EventManager.onWindowResize(this.onResize, this);
37078 this.cpad = this.el.getPadding("lr");
37079 this.hiddenCount = 0;
37082 // toolbar on the tabbar support...
37083 if (this.toolbar) {
37084 alert("no toolbar support yet");
37085 this.toolbar = false;
37087 var tcfg = this.toolbar;
37088 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
37089 this.toolbar = new Roo.Toolbar(tcfg);
37090 if (Roo.isSafari) {
37091 var tbl = tcfg.container.child('table', true);
37092 tbl.setAttribute('width', '100%');
37100 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37103 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37105 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37107 tabPosition : "top",
37109 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37111 currentTabWidth : 0,
37113 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37117 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37121 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37123 preferredTabWidth : 175,
37125 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37127 resizeTabs : false,
37129 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37131 monitorResize : true,
37133 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
37138 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37139 * @param {String} id The id of the div to use <b>or create</b>
37140 * @param {String} text The text for the tab
37141 * @param {String} content (optional) Content to put in the TabPanelItem body
37142 * @param {Boolean} closable (optional) True to create a close icon on the tab
37143 * @return {Roo.TabPanelItem} The created TabPanelItem
37145 addTab : function(id, text, content, closable, tpl)
37147 var item = new Roo.bootstrap.panel.TabItem({
37151 closable : closable,
37154 this.addTabItem(item);
37156 item.setContent(content);
37162 * Returns the {@link Roo.TabPanelItem} with the specified id/index
37163 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37164 * @return {Roo.TabPanelItem}
37166 getTab : function(id){
37167 return this.items[id];
37171 * Hides the {@link Roo.TabPanelItem} with the specified id/index
37172 * @param {String/Number} id The id or index of the TabPanelItem to hide.
37174 hideTab : function(id){
37175 var t = this.items[id];
37178 this.hiddenCount++;
37179 this.autoSizeTabs();
37184 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37185 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37187 unhideTab : function(id){
37188 var t = this.items[id];
37190 t.setHidden(false);
37191 this.hiddenCount--;
37192 this.autoSizeTabs();
37197 * Adds an existing {@link Roo.TabPanelItem}.
37198 * @param {Roo.TabPanelItem} item The TabPanelItem to add
37200 addTabItem : function(item){
37201 this.items[item.id] = item;
37202 this.items.push(item);
37203 // if(this.resizeTabs){
37204 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37205 // this.autoSizeTabs();
37207 // item.autoSize();
37212 * Removes a {@link Roo.TabPanelItem}.
37213 * @param {String/Number} id The id or index of the TabPanelItem to remove.
37215 removeTab : function(id){
37216 var items = this.items;
37217 var tab = items[id];
37218 if(!tab) { return; }
37219 var index = items.indexOf(tab);
37220 if(this.active == tab && items.length > 1){
37221 var newTab = this.getNextAvailable(index);
37226 this.stripEl.dom.removeChild(tab.pnode.dom);
37227 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37228 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37230 items.splice(index, 1);
37231 delete this.items[tab.id];
37232 tab.fireEvent("close", tab);
37233 tab.purgeListeners();
37234 this.autoSizeTabs();
37237 getNextAvailable : function(start){
37238 var items = this.items;
37240 // look for a next tab that will slide over to
37241 // replace the one being removed
37242 while(index < items.length){
37243 var item = items[++index];
37244 if(item && !item.isHidden()){
37248 // if one isn't found select the previous tab (on the left)
37251 var item = items[--index];
37252 if(item && !item.isHidden()){
37260 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37261 * @param {String/Number} id The id or index of the TabPanelItem to disable.
37263 disableTab : function(id){
37264 var tab = this.items[id];
37265 if(tab && this.active != tab){
37271 * Enables a {@link Roo.TabPanelItem} that is disabled.
37272 * @param {String/Number} id The id or index of the TabPanelItem to enable.
37274 enableTab : function(id){
37275 var tab = this.items[id];
37280 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37281 * @param {String/Number} id The id or index of the TabPanelItem to activate.
37282 * @return {Roo.TabPanelItem} The TabPanelItem.
37284 activate : function(id){
37285 var tab = this.items[id];
37289 if(tab == this.active || tab.disabled){
37293 this.fireEvent("beforetabchange", this, e, tab);
37294 if(e.cancel !== true && !tab.disabled){
37296 this.active.hide();
37298 this.active = this.items[id];
37299 this.active.show();
37300 this.fireEvent("tabchange", this, this.active);
37306 * Gets the active {@link Roo.TabPanelItem}.
37307 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37309 getActiveTab : function(){
37310 return this.active;
37314 * Updates the tab body element to fit the height of the container element
37315 * for overflow scrolling
37316 * @param {Number} targetHeight (optional) Override the starting height from the elements height
37318 syncHeight : function(targetHeight){
37319 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37320 var bm = this.bodyEl.getMargins();
37321 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37322 this.bodyEl.setHeight(newHeight);
37326 onResize : function(){
37327 if(this.monitorResize){
37328 this.autoSizeTabs();
37333 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37335 beginUpdate : function(){
37336 this.updating = true;
37340 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37342 endUpdate : function(){
37343 this.updating = false;
37344 this.autoSizeTabs();
37348 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37350 autoSizeTabs : function(){
37351 var count = this.items.length;
37352 var vcount = count - this.hiddenCount;
37353 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37356 var w = Math.max(this.el.getWidth() - this.cpad, 10);
37357 var availWidth = Math.floor(w / vcount);
37358 var b = this.stripBody;
37359 if(b.getWidth() > w){
37360 var tabs = this.items;
37361 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37362 if(availWidth < this.minTabWidth){
37363 /*if(!this.sleft){ // incomplete scrolling code
37364 this.createScrollButtons();
37367 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37370 if(this.currentTabWidth < this.preferredTabWidth){
37371 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37377 * Returns the number of tabs in this TabPanel.
37380 getCount : function(){
37381 return this.items.length;
37385 * Resizes all the tabs to the passed width
37386 * @param {Number} The new width
37388 setTabWidth : function(width){
37389 this.currentTabWidth = width;
37390 for(var i = 0, len = this.items.length; i < len; i++) {
37391 if(!this.items[i].isHidden()) {
37392 this.items[i].setWidth(width);
37398 * Destroys this TabPanel
37399 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
37401 destroy : function(removeEl){
37402 Roo.EventManager.removeResizeListener(this.onResize, this);
37403 for(var i = 0, len = this.items.length; i < len; i++){
37404 this.items[i].purgeListeners();
37406 if(removeEl === true){
37407 this.el.update("");
37412 createStrip : function(container)
37414 var strip = document.createElement("nav");
37415 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
37416 container.appendChild(strip);
37420 createStripList : function(strip)
37422 // div wrapper for retard IE
37423 // returns the "tr" element.
37424 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
37425 //'<div class="x-tabs-strip-wrap">'+
37426 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
37427 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
37428 return strip.firstChild; //.firstChild.firstChild.firstChild;
37430 createBody : function(container)
37432 var body = document.createElement("div");
37433 Roo.id(body, "tab-body");
37434 //Roo.fly(body).addClass("x-tabs-body");
37435 Roo.fly(body).addClass("tab-content");
37436 container.appendChild(body);
37439 createItemBody :function(bodyEl, id){
37440 var body = Roo.getDom(id);
37442 body = document.createElement("div");
37445 //Roo.fly(body).addClass("x-tabs-item-body");
37446 Roo.fly(body).addClass("tab-pane");
37447 bodyEl.insertBefore(body, bodyEl.firstChild);
37451 createStripElements : function(stripEl, text, closable, tpl)
37453 var td = document.createElement("li"); // was td..
37456 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
37459 stripEl.appendChild(td);
37461 td.className = "x-tabs-closable";
37462 if(!this.closeTpl){
37463 this.closeTpl = new Roo.Template(
37464 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37465 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
37466 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
37469 var el = this.closeTpl.overwrite(td, {"text": text});
37470 var close = el.getElementsByTagName("div")[0];
37471 var inner = el.getElementsByTagName("em")[0];
37472 return {"el": el, "close": close, "inner": inner};
37475 // not sure what this is..
37476 // if(!this.tabTpl){
37477 //this.tabTpl = new Roo.Template(
37478 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37479 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
37481 // this.tabTpl = new Roo.Template(
37482 // '<a href="#">' +
37483 // '<span unselectable="on"' +
37484 // (this.disableTooltips ? '' : ' title="{text}"') +
37485 // ' >{text}</span></a>'
37491 var template = tpl || this.tabTpl || false;
37495 template = new Roo.Template(
37497 '<span unselectable="on"' +
37498 (this.disableTooltips ? '' : ' title="{text}"') +
37499 ' >{text}</span></a>'
37503 switch (typeof(template)) {
37507 template = new Roo.Template(template);
37513 var el = template.overwrite(td, {"text": text});
37515 var inner = el.getElementsByTagName("span")[0];
37517 return {"el": el, "inner": inner};
37525 * @class Roo.TabPanelItem
37526 * @extends Roo.util.Observable
37527 * Represents an individual item (tab plus body) in a TabPanel.
37528 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
37529 * @param {String} id The id of this TabPanelItem
37530 * @param {String} text The text for the tab of this TabPanelItem
37531 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
37533 Roo.bootstrap.panel.TabItem = function(config){
37535 * The {@link Roo.TabPanel} this TabPanelItem belongs to
37536 * @type Roo.TabPanel
37538 this.tabPanel = config.panel;
37540 * The id for this TabPanelItem
37543 this.id = config.id;
37545 this.disabled = false;
37547 this.text = config.text;
37549 this.loaded = false;
37550 this.closable = config.closable;
37553 * The body element for this TabPanelItem.
37554 * @type Roo.Element
37556 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
37557 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
37558 this.bodyEl.setStyle("display", "block");
37559 this.bodyEl.setStyle("zoom", "1");
37560 //this.hideAction();
37562 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
37564 this.el = Roo.get(els.el);
37565 this.inner = Roo.get(els.inner, true);
37566 this.textEl = Roo.get(this.el.dom.firstChild, true);
37567 this.pnode = Roo.get(els.el.parentNode, true);
37568 this.el.on("mousedown", this.onTabMouseDown, this);
37569 this.el.on("click", this.onTabClick, this);
37571 if(config.closable){
37572 var c = Roo.get(els.close, true);
37573 c.dom.title = this.closeText;
37574 c.addClassOnOver("close-over");
37575 c.on("click", this.closeClick, this);
37581 * Fires when this tab becomes the active tab.
37582 * @param {Roo.TabPanel} tabPanel The parent TabPanel
37583 * @param {Roo.TabPanelItem} this
37587 * @event beforeclose
37588 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
37589 * @param {Roo.TabPanelItem} this
37590 * @param {Object} e Set cancel to true on this object to cancel the close.
37592 "beforeclose": true,
37595 * Fires when this tab is closed.
37596 * @param {Roo.TabPanelItem} this
37600 * @event deactivate
37601 * Fires when this tab is no longer the active tab.
37602 * @param {Roo.TabPanel} tabPanel The parent TabPanel
37603 * @param {Roo.TabPanelItem} this
37605 "deactivate" : true
37607 this.hidden = false;
37609 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
37612 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
37614 purgeListeners : function(){
37615 Roo.util.Observable.prototype.purgeListeners.call(this);
37616 this.el.removeAllListeners();
37619 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
37622 this.pnode.addClass("active");
37625 this.tabPanel.stripWrap.repaint();
37627 this.fireEvent("activate", this.tabPanel, this);
37631 * Returns true if this tab is the active tab.
37632 * @return {Boolean}
37634 isActive : function(){
37635 return this.tabPanel.getActiveTab() == this;
37639 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
37642 this.pnode.removeClass("active");
37644 this.fireEvent("deactivate", this.tabPanel, this);
37647 hideAction : function(){
37648 this.bodyEl.hide();
37649 this.bodyEl.setStyle("position", "absolute");
37650 this.bodyEl.setLeft("-20000px");
37651 this.bodyEl.setTop("-20000px");
37654 showAction : function(){
37655 this.bodyEl.setStyle("position", "relative");
37656 this.bodyEl.setTop("");
37657 this.bodyEl.setLeft("");
37658 this.bodyEl.show();
37662 * Set the tooltip for the tab.
37663 * @param {String} tooltip The tab's tooltip
37665 setTooltip : function(text){
37666 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
37667 this.textEl.dom.qtip = text;
37668 this.textEl.dom.removeAttribute('title');
37670 this.textEl.dom.title = text;
37674 onTabClick : function(e){
37675 e.preventDefault();
37676 this.tabPanel.activate(this.id);
37679 onTabMouseDown : function(e){
37680 e.preventDefault();
37681 this.tabPanel.activate(this.id);
37684 getWidth : function(){
37685 return this.inner.getWidth();
37688 setWidth : function(width){
37689 var iwidth = width - this.pnode.getPadding("lr");
37690 this.inner.setWidth(iwidth);
37691 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
37692 this.pnode.setWidth(width);
37696 * Show or hide the tab
37697 * @param {Boolean} hidden True to hide or false to show.
37699 setHidden : function(hidden){
37700 this.hidden = hidden;
37701 this.pnode.setStyle("display", hidden ? "none" : "");
37705 * Returns true if this tab is "hidden"
37706 * @return {Boolean}
37708 isHidden : function(){
37709 return this.hidden;
37713 * Returns the text for this tab
37716 getText : function(){
37720 autoSize : function(){
37721 //this.el.beginMeasure();
37722 this.textEl.setWidth(1);
37724 * #2804 [new] Tabs in Roojs
37725 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
37727 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
37728 //this.el.endMeasure();
37732 * Sets the text for the tab (Note: this also sets the tooltip text)
37733 * @param {String} text The tab's text and tooltip
37735 setText : function(text){
37737 this.textEl.update(text);
37738 this.setTooltip(text);
37739 //if(!this.tabPanel.resizeTabs){
37740 // this.autoSize();
37744 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
37746 activate : function(){
37747 this.tabPanel.activate(this.id);
37751 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
37753 disable : function(){
37754 if(this.tabPanel.active != this){
37755 this.disabled = true;
37756 this.pnode.addClass("disabled");
37761 * Enables this TabPanelItem if it was previously disabled.
37763 enable : function(){
37764 this.disabled = false;
37765 this.pnode.removeClass("disabled");
37769 * Sets the content for this TabPanelItem.
37770 * @param {String} content The content
37771 * @param {Boolean} loadScripts true to look for and load scripts
37773 setContent : function(content, loadScripts){
37774 this.bodyEl.update(content, loadScripts);
37778 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
37779 * @return {Roo.UpdateManager} The UpdateManager
37781 getUpdateManager : function(){
37782 return this.bodyEl.getUpdateManager();
37786 * Set a URL to be used to load the content for this TabPanelItem.
37787 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
37788 * @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)
37789 * @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)
37790 * @return {Roo.UpdateManager} The UpdateManager
37792 setUrl : function(url, params, loadOnce){
37793 if(this.refreshDelegate){
37794 this.un('activate', this.refreshDelegate);
37796 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37797 this.on("activate", this.refreshDelegate);
37798 return this.bodyEl.getUpdateManager();
37802 _handleRefresh : function(url, params, loadOnce){
37803 if(!loadOnce || !this.loaded){
37804 var updater = this.bodyEl.getUpdateManager();
37805 updater.update(url, params, this._setLoaded.createDelegate(this));
37810 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
37811 * Will fail silently if the setUrl method has not been called.
37812 * This does not activate the panel, just updates its content.
37814 refresh : function(){
37815 if(this.refreshDelegate){
37816 this.loaded = false;
37817 this.refreshDelegate();
37822 _setLoaded : function(){
37823 this.loaded = true;
37827 closeClick : function(e){
37830 this.fireEvent("beforeclose", this, o);
37831 if(o.cancel !== true){
37832 this.tabPanel.removeTab(this.id);
37836 * The text displayed in the tooltip for the close icon.
37839 closeText : "Close this tab"
37849 * @class Roo.bootstrap.PhoneInput
37850 * @extends Roo.bootstrap.TriggerField
37851 * Bootstrap PhoneInput class
37854 * Create a new PhoneInput
37855 * @param {Object} config The config object
37858 Roo.bootstrap.PhoneInput = function(config){
37860 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
37865 * Fires when the dropdown list is expanded
37866 * @param {Roo.bootstrap.ComboBox} combo This combo box
37871 * Fires when the dropdown list is collapsed
37872 * @param {Roo.bootstrap.ComboBox} combo This combo box
37876 * @event beforeselect
37877 * Fires before a list item is selected. Return false to cancel the selection.
37878 * @param {Roo.bootstrap.ComboBox} combo This combo box
37879 * @param {Roo.data.Record} record The data record returned from the underlying store
37880 * @param {Number} index The index of the selected item in the dropdown list
37882 'beforeselect' : true,
37885 * Fires when a list item is selected
37886 * @param {Roo.bootstrap.ComboBox} combo This combo box
37887 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
37888 * @param {Number} index The index of the selected item in the dropdown list
37892 * @event beforequery
37893 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
37894 * The event object passed has these properties:
37895 * @param {Roo.bootstrap.ComboBox} combo This combo box
37896 * @param {String} query The query
37897 * @param {Boolean} forceAll true to force "all" query
37898 * @param {Boolean} cancel true to cancel the query
37899 * @param {Object} e The query event object
37901 'beforequery': true,
37904 * Fires when the 'add' icon is pressed (add a listener to enable add button)
37905 * @param {Roo.bootstrap.ComboBox} combo This combo box
37910 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
37911 * @param {Roo.bootstrap.ComboBox} combo This combo box
37912 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
37917 * Fires when the remove value from the combobox array
37918 * @param {Roo.bootstrap.ComboBox} combo This combo box
37922 * @event afterremove
37923 * Fires when the remove value from the combobox array
37924 * @param {Roo.bootstrap.ComboBox} combo This combo box
37926 'afterremove' : true,
37928 * @event specialfilter
37929 * Fires when specialfilter
37930 * @param {Roo.bootstrap.ComboBox} combo This combo box
37932 'touchviewdisplay' : true
37935 this.country = []; //fetch country JSON
37938 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
37940 listWidth: undefined,
37944 selectedClass: 'active',
37950 triggerAction: 'query',
37952 validClass : "has-success",
37954 invalidClass: "has-warning",
37957 defaultCountry: 'hk',
37959 preferedCountries: undefined, //array
37961 filterCountries: undefined, //array
37963 displayMode: undefined, //string
37965 getAutoCreate : function(){
37967 this.list = Roo.bootstrap.PhoneInput.List;
37969 if(this.filterCountries) {
37970 for(var i = 0; i < this.filterCountries.length; i++) {
37971 delete this.list[this.filterCountries[i]];
37975 if (this.preferedCountries) {
37979 var align = this.labelAlign || this.parentLabelAlign();
37981 var id = Roo.id(); //all el??
37990 type : this.inputType,
37991 cls : 'form-control',
37992 style: 'padding-left: 60px;',
37993 placeholder : this.placeholder || ''
37997 input.name = this.name;
38000 input.cls += ' input-' + this.size;
38003 if (this.disabled) {
38004 input.disabled=true;
38007 var inputblock = input;
38009 if(this.hasFeedback && !this.allowBlank){
38012 cls: 'glyphicon form-control-feedback'
38020 inputblock.cn.push(input);
38022 if(this.hasFeedback && !this.allowBlank){
38023 inputblock.cls += 'has-feedback';
38024 inputblock.cn.push(feedback);
38033 cls: 'form-hidden-field'
38042 style: 'margin-right:5px',
38043 cls: 'roo-selected-region',
38044 cn: [] //flag position ... (iti-flag-us)
38052 if (this.caret != false) {
38055 cls: 'fa fa-' + this.caret
38060 cls: 'roo-select2-container input-group',
38066 cls : 'input-group-addon btn dropdown-toggle',
38067 style : 'position: absolute; z-index: 4;background: none;border: none; margin-top: 4px; margin-left: 3px; margin-right: 3px;',
38073 cls: 'combobox-clear',
38084 combobox.cn.push(box);
38086 if (align ==='left' && this.fieldLabel.length) {
38088 cfg.cls += ' roo-form-group-label-left';
38093 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
38094 tooltip : 'This field is required'
38099 cls : 'control-label',
38100 html : this.fieldLabel
38111 var labelCfg = cfg.cn[1];
38112 var contentCfg = cfg.cn[2];
38114 if(this.indicatorpos == 'right'){
38119 cls : 'control-label',
38123 html : this.fieldLabel
38127 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
38128 tooltip : 'This field is required'
38141 labelCfg = cfg.cn[0];
38142 contentCfg = cfg.cn[1];
38145 if(this.labelWidth > 12){
38146 labelCfg.style = "width: " + this.labelWidth + 'px';
38149 if(this.labelWidth < 13 && this.labelmd == 0){
38150 this.labelmd = this.labelWidth;
38153 if(this.labellg > 0){
38154 labelCfg.cls += ' col-lg-' + this.labellg;
38155 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
38158 if(this.labelmd > 0){
38159 labelCfg.cls += ' col-md-' + this.labelmd;
38160 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
38163 if(this.labelsm > 0){
38164 labelCfg.cls += ' col-sm-' + this.labelsm;
38165 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
38168 if(this.labelxs > 0){
38169 labelCfg.cls += ' col-xs-' + this.labelxs;
38170 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
38173 } else if ( this.fieldLabel.length) {
38174 // Roo.log(" label");
38178 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
38179 tooltip : 'This field is required'
38183 //cls : 'input-group-addon',
38184 html : this.fieldLabel
38192 if(this.indicatorpos == 'right'){
38200 html : this.fieldLabel
38204 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
38205 tooltip : 'This field is required'
38217 ['xs','sm','md','lg'].map(function(size){
38218 if (settings[size]) {
38219 cfg.cls += ' col-' + size + '-' + settings[size];
38226 _initEventsCalled : false,
38228 initEvents: function()
38230 if (this._initEventsCalled) {
38234 this._initEventsCalled = true;
38236 this.store = new Roo.data.SimpleStore({
38238 fields : ['name','iso','dial_code','order','area_code']
38241 this.store = Roo.factory(this.store, Roo.data);
38242 this.store.parent = this;
38244 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
38249 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
38250 _this.list.setWidth(lw);
38253 this.list.on('mouseover', this.onViewOver, this);
38254 this.list.on('mousemove', this.onViewMove, this);
38255 this.list.on('scroll', this.onViewScroll, this);
38258 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
38261 this.view = new Roo.View(this.list, this.tpl, {
38262 singleSelect:true, store: this.store, selectedClass: this.selectedClass
38265 this.view.on('click', this.onViewClick, this);
38267 this.store.on('beforeload', this.onBeforeLoad, this);
38268 this.store.on('load', this.onLoad, this);
38269 this.store.on('loadexception', this.onLoadException, this);
38271 this.keyNav = new Roo.KeyNav(this.inputEl(), {
38272 "up" : function(e){
38273 this.inKeyMode = true;
38277 "down" : function(e){
38278 if(!this.isExpanded()){
38279 this.onTriggerClick();
38281 this.inKeyMode = true;
38286 "enter" : function(e){
38287 // this.onViewClick();
38291 if(this.fireEvent("specialkey", this, e)){
38292 this.onViewClick(false);
38298 "esc" : function(e){
38302 "tab" : function(e){
38305 if(this.fireEvent("specialkey", this, e)){
38306 this.onViewClick(false);
38314 doRelay : function(foo, bar, hname){
38315 if(hname == 'down' || this.scope.isExpanded()){
38316 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
38326 onViewOver : function(e, t){
38327 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
38330 var item = this.view.findItemFromChild(t);
38333 var index = this.view.indexOf(item);
38334 this.select(index, false);
38338 onViewMove : function(e, t){
38339 this.inKeyMode = false;
38342 onViewScroll : function(e, t){
38344 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){
38348 this.hasQuery = true;
38350 this.loading = this.list.select('.loading', true).first();
38352 if(this.loading === null){
38353 this.list.createChild({
38355 cls: 'loading roo-select2-more-results roo-select2-active',
38356 html: 'Loading more results...'
38359 this.loading = this.list.select('.loading', true).first();
38361 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
38363 this.loading.hide();
38366 this.loading.show();
38371 this.loadNext = true;
38373 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
38378 onTriggerClick : function(e)
38380 Roo.log('trigger click');
38382 if(this.disabled || !this.triggerList){
38387 this.loadNext = false;
38389 if(this.isExpanded()){
38391 if (!this.blockFocus) {
38392 this.inputEl().focus();
38396 this.hasFocus = true;
38397 if(this.triggerAction == 'all') {
38398 this.doQuery(this.allQuery, true);
38400 this.doQuery(this.getRawValue());
38402 if (!this.blockFocus) {
38403 this.inputEl().focus();
38410 Roo.apply(Roo.bootstrap.PhoneInput, {
38413 * iso2 and abbr for all countries
38417 ["Afghanistan (افغانستان)", "af", "93"],
38418 ["Albania (Shqipëri)", "al", "355"],
38419 ["Algeria (الجزائر)", "dz", "213"],
38420 ["American Samoa", "as", "1684"],
38421 ["Andorra", "ad", "376"],
38422 ["Angola", "ao", "244"],
38423 ["Anguilla", "ai", "1264"],
38424 ["Antigua and Barbuda", "ag", "1268"],
38425 ["Argentina", "ar", "54"],
38426 ["Armenia (Հայաստան)", "am", "374"],
38427 ["Aruba", "aw", "297"],
38428 ["Australia", "au", "61", 0],
38429 ["Austria (Österreich)", "at", "43"],
38430 ["Azerbaijan (Azərbaycan)", "az", "994"],
38431 ["Bahamas", "bs", "1242"],
38432 ["Bahrain (البحرين)", "bh", "973"],
38433 ["Bangladesh (বাংলাদেশ)", "bd", "880"],
38434 ["Barbados", "bb", "1246"],
38435 ["Belarus (Беларусь)", "by", "375"],
38436 ["Belgium (België)", "be", "32"],
38437 ["Belize", "bz", "501"],
38438 ["Benin (Bénin)", "bj", "229"],
38439 ["Bermuda", "bm", "1441"],
38440 ["Bhutan (འབྲུག)", "bt", "975"],
38441 ["Bolivia", "bo", "591"],
38442 ["Bosnia and Herzegovina (Босна и Херцеговина)", "ba", "387"],
38443 ["Botswana", "bw", "267"],
38444 ["Brazil (Brasil)", "br", "55"],
38445 ["British Indian Ocean Territory", "io", "246"],
38446 ["British Virgin Islands", "vg", "1284"],
38447 ["Brunei", "bn", "673"],
38448 ["Bulgaria (България)", "bg", "359"],
38449 ["Burkina Faso", "bf", "226"],
38450 ["Burundi (Uburundi)", "bi", "257"],
38451 ["Cambodia (កម្ពុជា)", "kh", "855"],
38452 ["Cameroon (Cameroun)", "cm", "237"],
38453 ["Canada", "ca", "1", 1, ["204", "226", "236", "249", "250", "289", "306", "343", "365", "387", "403", "416", "418", "431", "437", "438", "450", "506", "514", "519", "548", "579", "581", "587", "604", "613", "639", "647", "672", "705", "709", "742", "778", "780", "782", "807", "819", "825", "867", "873", "902", "905"]],
38454 ["Cape Verde (Kabu Verdi)", "cv", "238"],
38455 ["Caribbean Netherlands", "bq", "599", 1],
38456 ["Cayman Islands", "ky", "1345"],
38457 ["Central African Republic (République centrafricaine)", "cf", "236"],
38458 ["Chad (Tchad)", "td", "235"],
38459 ["Chile", "cl", "56"],
38460 ["China (中国)", "cn", "86"],
38461 ["Christmas Island", "cx", "61", 2],
38462 ["Cocos (Keeling) Islands", "cc", "61", 1],
38463 ["Colombia", "co", "57"],
38464 ["Comoros (جزر القمر)", "km", "269"],
38465 ["Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)", "cd", "243"],
38466 ["Congo (Republic) (Congo-Brazzaville)", "cg", "242"],
38467 ["Cook Islands", "ck", "682"],
38468 ["Costa Rica", "cr", "506"],
38469 ["Côte d’Ivoire", "ci", "225"],
38470 ["Croatia (Hrvatska)", "hr", "385"],
38471 ["Cuba", "cu", "53"],
38472 ["Curaçao", "cw", "599", 0],
38473 ["Cyprus (Κύπρος)", "cy", "357"],
38474 ["Czech Republic (Česká republika)", "cz", "420"],
38475 ["Denmark (Danmark)", "dk", "45"],
38476 ["Djibouti", "dj", "253"],
38477 ["Dominica", "dm", "1767"],
38478 ["Dominican Republic (República Dominicana)", "do", "1", 2, ["809", "829", "849"]],
38479 ["Ecuador", "ec", "593"],
38480 ["Egypt (مصر)", "eg", "20"],
38481 ["El Salvador", "sv", "503"],
38482 ["Equatorial Guinea (Guinea Ecuatorial)", "gq", "240"],
38483 ["Eritrea", "er", "291"],
38484 ["Estonia (Eesti)", "ee", "372"],
38485 ["Ethiopia", "et", "251"],
38486 ["Falkland Islands (Islas Malvinas)", "fk", "500"],
38487 ["Faroe Islands (Føroyar)", "fo", "298"],
38488 ["Fiji", "fj", "679"],
38489 ["Finland (Suomi)", "fi", "358", 0],
38490 ["France", "fr", "33"],
38491 ["French Guiana (Guyane française)", "gf", "594"],
38492 ["French Polynesia (Polynésie française)", "pf", "689"],
38493 ["Gabon", "ga", "241"],
38494 ["Gambia", "gm", "220"],
38495 ["Georgia (საქართველო)", "ge", "995"],
38496 ["Germany (Deutschland)", "de", "49"],
38497 ["Ghana (Gaana)", "gh", "233"],
38498 ["Gibraltar", "gi", "350"],
38499 ["Greece (Ελλάδα)", "gr", "30"],
38500 ["Greenland (Kalaallit Nunaat)", "gl", "299"],
38501 ["Grenada", "gd", "1473"],
38502 ["Guadeloupe", "gp", "590", 0],
38503 ["Guam", "gu", "1671"],
38504 ["Guatemala", "gt", "502"],
38505 ["Guernsey", "gg", "44", 1],
38506 ["Guinea (Guinée)", "gn", "224"],
38507 ["Guinea-Bissau (Guiné Bissau)", "gw", "245"],
38508 ["Guyana", "gy", "592"],
38509 ["Haiti", "ht", "509"],
38510 ["Honduras", "hn", "504"],
38511 ["Hong Kong (香港)", "hk", "852"],
38512 ["Hungary (Magyarország)", "hu", "36"],
38513 ["Iceland (Ísland)", "is", "354"],
38514 ["India (भारत)", "in", "91"],
38515 ["Indonesia", "id", "62"],
38516 ["Iran (ایران)", "ir", "98"],
38517 ["Iraq (العراق)", "iq", "964"],
38518 ["Ireland", "ie", "353"],
38519 ["Isle of Man", "im", "44", 2],
38520 ["Israel (ישראל)", "il", "972"],
38521 ["Italy (Italia)", "it", "39", 0],
38522 ["Jamaica", "jm", "1876"],
38523 ["Japan (日本)", "jp", "81"],
38524 ["Jersey", "je", "44", 3],
38525 ["Jordan (الأردن)", "jo", "962"],
38526 ["Kazakhstan (Казахстан)", "kz", "7", 1],
38527 ["Kenya", "ke", "254"],
38528 ["Kiribati", "ki", "686"],
38529 ["Kosovo", "xk", "383"],
38530 ["Kuwait (الكويت)", "kw", "965"],
38531 ["Kyrgyzstan (Кыргызстан)", "kg", "996"],
38532 ["Laos (ລາວ)", "la", "856"],
38533 ["Latvia (Latvija)", "lv", "371"],
38534 ["Lebanon (لبنان)", "lb", "961"],
38535 ["Lesotho", "ls", "266"],
38536 ["Liberia", "lr", "231"],
38537 ["Libya (ليبيا)", "ly", "218"],
38538 ["Liechtenstein", "li", "423"],
38539 ["Lithuania (Lietuva)", "lt", "370"],
38540 ["Luxembourg", "lu", "352"],
38541 ["Macau (澳門)", "mo", "853"],
38542 ["Macedonia (FYROM) (Македонија)", "mk", "389"],
38543 ["Madagascar (Madagasikara)", "mg", "261"],
38544 ["Malawi", "mw", "265"],
38545 ["Malaysia", "my", "60"],
38546 ["Maldives", "mv", "960"],
38547 ["Mali", "ml", "223"],
38548 ["Malta", "mt", "356"],
38549 ["Marshall Islands", "mh", "692"],
38550 ["Martinique", "mq", "596"],
38551 ["Mauritania (موريتانيا)", "mr", "222"],
38552 ["Mauritius (Moris)", "mu", "230"],
38553 ["Mayotte", "yt", "262", 1],
38554 ["Mexico (México)", "mx", "52"],
38555 ["Micronesia", "fm", "691"],
38556 ["Moldova (Republica Moldova)", "md", "373"],
38557 ["Monaco", "mc", "377"],
38558 ["Mongolia (Монгол)", "mn", "976"],
38559 ["Montenegro (Crna Gora)", "me", "382"],
38560 ["Montserrat", "ms", "1664"],
38561 ["Morocco (المغرب)", "ma", "212", 0],
38562 ["Mozambique (Moçambique)", "mz", "258"],
38563 ["Myanmar (Burma) (မြန်မာ)", "mm", "95"],
38564 ["Namibia (Namibië)", "na", "264"],
38565 ["Nauru", "nr", "674"],
38566 ["Nepal (नेपाल)", "np", "977"],
38567 ["Netherlands (Nederland)", "nl", "31"],
38568 ["New Caledonia (Nouvelle-Calédonie)", "nc", "687"],
38569 ["New Zealand", "nz", "64"],
38570 ["Nicaragua", "ni", "505"],
38571 ["Niger (Nijar)", "ne", "227"],
38572 ["Nigeria", "ng", "234"],
38573 ["Niue", "nu", "683"],
38574 ["Norfolk Island", "nf", "672"],
38575 ["North Korea (조선 민주주의 인민 공화국)", "kp", "850"],
38576 ["Northern Mariana Islands", "mp", "1670"],
38577 ["Norway (Norge)", "no", "47", 0],
38578 ["Oman (عُمان)", "om", "968"],
38579 ["Pakistan (پاکستان)", "pk", "92"],
38580 ["Palau", "pw", "680"],
38581 ["Palestine (فلسطين)", "ps", "970"],
38582 ["Panama (Panamá)", "pa", "507"],
38583 ["Papua New Guinea", "pg", "675"],
38584 ["Paraguay", "py", "595"],
38585 ["Peru (Perú)", "pe", "51"],
38586 ["Philippines", "ph", "63"],
38587 ["Poland (Polska)", "pl", "48"],
38588 ["Portugal", "pt", "351"],
38589 ["Puerto Rico", "pr", "1", 3, ["787", "939"]],
38590 ["Qatar (قطر)", "qa", "974"],
38591 ["Réunion (La Réunion)", "re", "262", 0],
38592 ["Romania (România)", "ro", "40"],
38593 ["Russia (Россия)", "ru", "7", 0],
38594 ["Rwanda", "rw", "250"],
38595 ["Saint Barthélemy", "bl", "590", 1],
38596 ["Saint Helena", "sh", "290"],
38597 ["Saint Kitts and Nevis", "kn", "1869"],
38598 ["Saint Lucia", "lc", "1758"],
38599 ["Saint Martin (Saint-Martin (partie française))", "mf", "590", 2],
38600 ["Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)", "pm", "508"],
38601 ["Saint Vincent and the Grenadines", "vc", "1784"],
38602 ["Samoa", "ws", "685"],
38603 ["San Marino", "sm", "378"],
38604 ["São Tomé and Príncipe (São Tomé e Príncipe)", "st", "239"],
38605 ["Saudi Arabia (المملكة العربية السعودية)", "sa", "966"],
38606 ["Senegal (Sénégal)", "sn", "221"],
38607 ["Serbia (Србија)", "rs", "381"],
38608 ["Seychelles", "sc", "248"],
38609 ["Sierra Leone", "sl", "232"],
38610 ["Singapore", "sg", "65"],
38611 ["Sint Maarten", "sx", "1721"],
38612 ["Slovakia (Slovensko)", "sk", "421"],
38613 ["Slovenia (Slovenija)", "si", "386"],
38614 ["Solomon Islands", "sb", "677"],
38615 ["Somalia (Soomaaliya)", "so", "252"],
38616 ["South Africa", "za", "27"],
38617 ["South Korea (대한민국)", "kr", "82"],
38618 ["South Sudan (جنوب السودان)", "ss", "211"],
38619 ["Spain (España)", "es", "34"],
38620 ["Sri Lanka (ශ්රී ලංකාව)", "lk", "94"],
38621 ["Sudan (السودان)", "sd", "249"],
38622 ["Suriname", "sr", "597"],
38623 ["Svalbard and Jan Mayen", "sj", "47", 1],
38624 ["Swaziland", "sz", "268"],
38625 ["Sweden (Sverige)", "se", "46"],
38626 ["Switzerland (Schweiz)", "ch", "41"],
38627 ["Syria (سوريا)", "sy", "963"],
38628 ["Taiwan (台灣)", "tw", "886"],
38629 ["Tajikistan", "tj", "992"],
38630 ["Tanzania", "tz", "255"],
38631 ["Thailand (ไทย)", "th", "66"],
38632 ["Timor-Leste", "tl", "670"],
38633 ["Togo", "tg", "228"],
38634 ["Tokelau", "tk", "690"],
38635 ["Tonga", "to", "676"],
38636 ["Trinidad and Tobago", "tt", "1868"],
38637 ["Tunisia (تونس)", "tn", "216"],
38638 ["Turkey (Türkiye)", "tr", "90"],
38639 ["Turkmenistan", "tm", "993"],
38640 ["Turks and Caicos Islands", "tc", "1649"],
38641 ["Tuvalu", "tv", "688"],
38642 ["U.S. Virgin Islands", "vi", "1340"],
38643 ["Uganda", "ug", "256"],
38644 ["Ukraine (Україна)", "ua", "380"],
38645 ["United Arab Emirates (الإمارات العربية المتحدة)", "ae", "971"],
38646 ["United Kingdom", "gb", "44", 0],
38647 ["United States", "us", "1", 0],
38648 ["Uruguay", "uy", "598"],
38649 ["Uzbekistan (Oʻzbekiston)", "uz", "998"],
38650 ["Vanuatu", "vu", "678"],
38651 ["Vatican City (Città del Vaticano)", "va", "39", 1],
38652 ["Venezuela", "ve", "58"],
38653 ["Vietnam (Việt Nam)", "vn", "84"],
38654 ["Wallis and Futuna (Wallis-et-Futuna)", "wf", "681"],
38655 ["Western Sahara (الصحراء الغربية)", "eh", "212", 1],
38656 ["Yemen (اليمن)", "ye", "967"],
38657 ["Zambia", "zm", "260"],
38658 ["Zimbabwe", "zw", "263"],
38659 ["Åland Islands", "ax", "358", 1]