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 (vv.length || vv * 1 > 0) ? close.show() : close.hide();
13712 if(this.hiddenField){
13713 this.hiddenField.dom.value = vv;
13715 this.lastSelectionText = dv;
13716 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13720 // no hidden field.. - we store the value in 'value', but still display
13721 // display field!!!!
13722 this.lastSelectionText = dv;
13723 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13730 reset : function(){
13731 // overridden so that last data is reset..
13738 this.setValue(this.originalValue);
13739 //this.clearInvalid();
13740 this.lastData = false;
13742 this.view.clearSelections();
13748 findRecord : function(prop, value){
13750 if(this.store.getCount() > 0){
13751 this.store.each(function(r){
13752 if(r.data[prop] == value){
13762 getName: function()
13764 // returns hidden if it's set..
13765 if (!this.rendered) {return ''};
13766 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
13770 onViewMove : function(e, t){
13771 this.inKeyMode = false;
13775 onViewOver : function(e, t){
13776 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13779 var item = this.view.findItemFromChild(t);
13782 var index = this.view.indexOf(item);
13783 this.select(index, false);
13788 onViewClick : function(view, doFocus, el, e)
13790 var index = this.view.getSelectedIndexes()[0];
13792 var r = this.store.getAt(index);
13796 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13803 Roo.each(this.tickItems, function(v,k){
13805 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13807 _this.tickItems.splice(k, 1);
13809 if(typeof(e) == 'undefined' && view == false){
13810 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13822 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13823 this.tickItems.push(r.data);
13826 if(typeof(e) == 'undefined' && view == false){
13827 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13834 this.onSelect(r, index);
13836 if(doFocus !== false && !this.blockFocus){
13837 this.inputEl().focus();
13842 restrictHeight : function(){
13843 //this.innerList.dom.style.height = '';
13844 //var inner = this.innerList.dom;
13845 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13846 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13847 //this.list.beginUpdate();
13848 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13849 this.list.alignTo(this.inputEl(), this.listAlign);
13850 this.list.alignTo(this.inputEl(), this.listAlign);
13851 //this.list.endUpdate();
13855 onEmptyResults : function(){
13857 if(this.tickable && this.editable){
13858 this.restrictHeight();
13866 * Returns true if the dropdown list is expanded, else false.
13868 isExpanded : function(){
13869 return this.list.isVisible();
13873 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13874 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13875 * @param {String} value The data value of the item to select
13876 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13877 * selected item if it is not currently in view (defaults to true)
13878 * @return {Boolean} True if the value matched an item in the list, else false
13880 selectByValue : function(v, scrollIntoView){
13881 if(v !== undefined && v !== null){
13882 var r = this.findRecord(this.valueField || this.displayField, v);
13884 this.select(this.store.indexOf(r), scrollIntoView);
13892 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13893 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13894 * @param {Number} index The zero-based index of the list item to select
13895 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13896 * selected item if it is not currently in view (defaults to true)
13898 select : function(index, scrollIntoView){
13899 this.selectedIndex = index;
13900 this.view.select(index);
13901 if(scrollIntoView !== false){
13902 var el = this.view.getNode(index);
13904 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13907 this.list.scrollChildIntoView(el, false);
13913 selectNext : function(){
13914 var ct = this.store.getCount();
13916 if(this.selectedIndex == -1){
13918 }else if(this.selectedIndex < ct-1){
13919 this.select(this.selectedIndex+1);
13925 selectPrev : function(){
13926 var ct = this.store.getCount();
13928 if(this.selectedIndex == -1){
13930 }else if(this.selectedIndex != 0){
13931 this.select(this.selectedIndex-1);
13937 onKeyUp : function(e){
13938 if(this.editable !== false && !e.isSpecialKey()){
13939 this.lastKey = e.getKey();
13940 this.dqTask.delay(this.queryDelay);
13945 validateBlur : function(){
13946 return !this.list || !this.list.isVisible();
13950 initQuery : function(){
13952 var v = this.getRawValue();
13954 if(this.tickable && this.editable){
13955 v = this.tickableInputEl().getValue();
13962 doForce : function(){
13963 if(this.inputEl().dom.value.length > 0){
13964 this.inputEl().dom.value =
13965 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
13971 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
13972 * query allowing the query action to be canceled if needed.
13973 * @param {String} query The SQL query to execute
13974 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
13975 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
13976 * saved in the current store (defaults to false)
13978 doQuery : function(q, forceAll){
13980 if(q === undefined || q === null){
13985 forceAll: forceAll,
13989 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
13994 forceAll = qe.forceAll;
13995 if(forceAll === true || (q.length >= this.minChars)){
13997 this.hasQuery = true;
13999 if(this.lastQuery != q || this.alwaysQuery){
14000 this.lastQuery = q;
14001 if(this.mode == 'local'){
14002 this.selectedIndex = -1;
14004 this.store.clearFilter();
14007 if(this.specialFilter){
14008 this.fireEvent('specialfilter', this);
14013 this.store.filter(this.displayField, q);
14016 this.store.fireEvent("datachanged", this.store);
14023 this.store.baseParams[this.queryParam] = q;
14025 var options = {params : this.getParams(q)};
14028 options.add = true;
14029 options.params.start = this.page * this.pageSize;
14032 this.store.load(options);
14035 * this code will make the page width larger, at the beginning, the list not align correctly,
14036 * we should expand the list on onLoad
14037 * so command out it
14042 this.selectedIndex = -1;
14047 this.loadNext = false;
14051 getParams : function(q){
14053 //p[this.queryParam] = q;
14057 p.limit = this.pageSize;
14063 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14065 collapse : function(){
14066 if(!this.isExpanded()){
14072 this.hasFocus = false;
14076 this.cancelBtn.hide();
14077 this.trigger.show();
14080 this.tickableInputEl().dom.value = '';
14081 this.tickableInputEl().blur();
14086 Roo.get(document).un('mousedown', this.collapseIf, this);
14087 Roo.get(document).un('mousewheel', this.collapseIf, this);
14088 if (!this.editable) {
14089 Roo.get(document).un('keydown', this.listKeyPress, this);
14091 this.fireEvent('collapse', this);
14097 collapseIf : function(e){
14098 var in_combo = e.within(this.el);
14099 var in_list = e.within(this.list);
14100 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14102 if (in_combo || in_list || is_list) {
14103 //e.stopPropagation();
14108 this.onTickableFooterButtonClick(e, false, false);
14116 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14118 expand : function(){
14120 if(this.isExpanded() || !this.hasFocus){
14124 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14125 this.list.setWidth(lw);
14131 this.restrictHeight();
14135 this.tickItems = Roo.apply([], this.item);
14138 this.cancelBtn.show();
14139 this.trigger.hide();
14142 this.tickableInputEl().focus();
14147 Roo.get(document).on('mousedown', this.collapseIf, this);
14148 Roo.get(document).on('mousewheel', this.collapseIf, this);
14149 if (!this.editable) {
14150 Roo.get(document).on('keydown', this.listKeyPress, this);
14153 this.fireEvent('expand', this);
14157 // Implements the default empty TriggerField.onTriggerClick function
14158 onTriggerClick : function(e)
14160 Roo.log('trigger click');
14162 if(this.disabled || !this.triggerList){
14167 this.loadNext = false;
14169 if(this.isExpanded()){
14171 if (!this.blockFocus) {
14172 this.inputEl().focus();
14176 this.hasFocus = true;
14177 if(this.triggerAction == 'all') {
14178 this.doQuery(this.allQuery, true);
14180 this.doQuery(this.getRawValue());
14182 if (!this.blockFocus) {
14183 this.inputEl().focus();
14188 onTickableTriggerClick : function(e)
14195 this.loadNext = false;
14196 this.hasFocus = true;
14198 if(this.triggerAction == 'all') {
14199 this.doQuery(this.allQuery, true);
14201 this.doQuery(this.getRawValue());
14205 onSearchFieldClick : function(e)
14207 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14208 this.onTickableFooterButtonClick(e, false, false);
14212 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14217 this.loadNext = false;
14218 this.hasFocus = true;
14220 if(this.triggerAction == 'all') {
14221 this.doQuery(this.allQuery, true);
14223 this.doQuery(this.getRawValue());
14227 listKeyPress : function(e)
14229 //Roo.log('listkeypress');
14230 // scroll to first matching element based on key pres..
14231 if (e.isSpecialKey()) {
14234 var k = String.fromCharCode(e.getKey()).toUpperCase();
14237 var csel = this.view.getSelectedNodes();
14238 var cselitem = false;
14240 var ix = this.view.indexOf(csel[0]);
14241 cselitem = this.store.getAt(ix);
14242 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14248 this.store.each(function(v) {
14250 // start at existing selection.
14251 if (cselitem.id == v.id) {
14257 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14258 match = this.store.indexOf(v);
14264 if (match === false) {
14265 return true; // no more action?
14268 this.view.select(match);
14269 var sn = Roo.get(this.view.getSelectedNodes()[0]);
14270 sn.scrollIntoView(sn.dom.parentNode, false);
14273 onViewScroll : function(e, t){
14275 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){
14279 this.hasQuery = true;
14281 this.loading = this.list.select('.loading', true).first();
14283 if(this.loading === null){
14284 this.list.createChild({
14286 cls: 'loading roo-select2-more-results roo-select2-active',
14287 html: 'Loading more results...'
14290 this.loading = this.list.select('.loading', true).first();
14292 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14294 this.loading.hide();
14297 this.loading.show();
14302 this.loadNext = true;
14304 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14309 addItem : function(o)
14311 var dv = ''; // display value
14313 if (this.displayField) {
14314 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14316 // this is an error condition!!!
14317 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14324 var choice = this.choices.createChild({
14326 cls: 'roo-select2-search-choice',
14335 cls: 'roo-select2-search-choice-close fa fa-times',
14340 }, this.searchField);
14342 var close = choice.select('a.roo-select2-search-choice-close', true).first();
14344 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14352 this.inputEl().dom.value = '';
14357 onRemoveItem : function(e, _self, o)
14359 e.preventDefault();
14361 this.lastItem = Roo.apply([], this.item);
14363 var index = this.item.indexOf(o.data) * 1;
14366 Roo.log('not this item?!');
14370 this.item.splice(index, 1);
14375 this.fireEvent('remove', this, e);
14381 syncValue : function()
14383 if(!this.item.length){
14390 Roo.each(this.item, function(i){
14391 if(_this.valueField){
14392 value.push(i[_this.valueField]);
14399 this.value = value.join(',');
14401 if(this.hiddenField){
14402 this.hiddenField.dom.value = this.value;
14405 this.store.fireEvent("datachanged", this.store);
14410 clearItem : function()
14412 if(!this.multiple){
14418 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14426 if(this.tickable && !Roo.isTouch){
14427 this.view.refresh();
14431 inputEl: function ()
14433 if(Roo.isIOS && this.useNativeIOS){
14434 return this.el.select('select.roo-ios-select', true).first();
14437 if(Roo.isTouch && this.mobileTouchView){
14438 return this.el.select('input.form-control',true).first();
14442 return this.searchField;
14445 return this.el.select('input.form-control',true).first();
14448 onTickableFooterButtonClick : function(e, btn, el)
14450 e.preventDefault();
14452 this.lastItem = Roo.apply([], this.item);
14454 if(btn && btn.name == 'cancel'){
14455 this.tickItems = Roo.apply([], this.item);
14464 Roo.each(this.tickItems, function(o){
14472 validate : function()
14474 var v = this.getRawValue();
14477 v = this.getValue();
14480 if(this.disabled || this.allowBlank || v.length){
14485 this.markInvalid();
14489 tickableInputEl : function()
14491 if(!this.tickable || !this.editable){
14492 return this.inputEl();
14495 return this.inputEl().select('.roo-select2-search-field-input', true).first();
14499 getAutoCreateTouchView : function()
14504 cls: 'form-group' //input-group
14510 type : this.inputType,
14511 cls : 'form-control x-combo-noedit',
14512 autocomplete: 'new-password',
14513 placeholder : this.placeholder || '',
14518 input.name = this.name;
14522 input.cls += ' input-' + this.size;
14525 if (this.disabled) {
14526 input.disabled = true;
14537 inputblock.cls += ' input-group';
14539 inputblock.cn.unshift({
14541 cls : 'input-group-addon',
14546 if(this.removable && !this.multiple){
14547 inputblock.cls += ' roo-removable';
14549 inputblock.cn.push({
14552 cls : 'roo-combo-removable-btn close'
14556 if(this.hasFeedback && !this.allowBlank){
14558 inputblock.cls += ' has-feedback';
14560 inputblock.cn.push({
14562 cls: 'glyphicon form-control-feedback'
14569 inputblock.cls += (this.before) ? '' : ' input-group';
14571 inputblock.cn.push({
14573 cls : 'input-group-addon',
14584 cls: 'form-hidden-field'
14598 cls: 'form-hidden-field'
14602 cls: 'roo-select2-choices',
14606 cls: 'roo-select2-search-field',
14619 cls: 'roo-select2-container input-group roo-touchview-combobox ',
14625 if(!this.multiple && this.showToggleBtn){
14632 if (this.caret != false) {
14635 cls: 'fa fa-' + this.caret
14642 cls : 'input-group-addon btn dropdown-toggle',
14647 cls: 'combobox-clear',
14661 combobox.cls += ' roo-select2-container-multi';
14664 var align = this.labelAlign || this.parentLabelAlign();
14666 if (align ==='left' && this.fieldLabel.length) {
14671 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14672 tooltip : 'This field is required'
14676 cls : 'control-label',
14677 html : this.fieldLabel
14688 var labelCfg = cfg.cn[1];
14689 var contentCfg = cfg.cn[2];
14692 if(this.indicatorpos == 'right'){
14696 cls : 'control-label',
14697 html : this.fieldLabel,
14701 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14702 tooltip : 'This field is required'
14715 labelCfg = cfg.cn[0];
14716 contentCfg = cfg.cn[2];
14718 if(this.labelWidth > 12){
14719 labelCfg.style = "width: " + this.labelWidth + 'px';
14722 if(this.labelWidth < 13 && this.labelmd == 0){
14723 this.labelmd = this.labelWidth;
14726 if(this.labellg > 0){
14727 labelCfg.cls += ' col-lg-' + this.labellg;
14728 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14731 if(this.labelmd > 0){
14732 labelCfg.cls += ' col-md-' + this.labelmd;
14733 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14736 if(this.labelsm > 0){
14737 labelCfg.cls += ' col-sm-' + this.labelsm;
14738 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14741 if(this.labelxs > 0){
14742 labelCfg.cls += ' col-xs-' + this.labelxs;
14743 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14747 } else if ( this.fieldLabel.length) {
14751 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14752 tooltip : 'This field is required'
14756 cls : 'control-label',
14757 html : this.fieldLabel
14768 if(this.indicatorpos == 'right'){
14772 cls : 'control-label',
14773 html : this.fieldLabel,
14777 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14778 tooltip : 'This field is required'
14795 var settings = this;
14797 ['xs','sm','md','lg'].map(function(size){
14798 if (settings[size]) {
14799 cfg.cls += ' col-' + size + '-' + settings[size];
14806 initTouchView : function()
14808 this.renderTouchView();
14810 this.touchViewEl.on('scroll', function(){
14811 this.el.dom.scrollTop = 0;
14814 this.originalValue = this.getValue();
14816 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14818 this.inputEl().on("click", this.showTouchView, this);
14819 if (this.triggerEl) {
14820 this.triggerEl.on("click", this.showTouchView, this);
14824 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14825 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14827 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14829 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14830 this.store.on('load', this.onTouchViewLoad, this);
14831 this.store.on('loadexception', this.onTouchViewLoadException, this);
14833 if(this.hiddenName){
14835 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14837 this.hiddenField.dom.value =
14838 this.hiddenValue !== undefined ? this.hiddenValue :
14839 this.value !== undefined ? this.value : '';
14841 this.el.dom.removeAttribute('name');
14842 this.hiddenField.dom.setAttribute('name', this.hiddenName);
14846 this.choices = this.el.select('ul.roo-select2-choices', true).first();
14847 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14850 if(this.removable && !this.multiple){
14851 var close = this.closeTriggerEl();
14853 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14854 close.on('click', this.removeBtnClick, this, close);
14858 * fix the bug in Safari iOS8
14860 this.inputEl().on("focus", function(e){
14861 document.activeElement.blur();
14869 renderTouchView : function()
14871 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14872 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14874 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14875 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14877 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14878 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14879 this.touchViewBodyEl.setStyle('overflow', 'auto');
14881 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14882 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14884 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14885 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14889 showTouchView : function()
14895 this.touchViewHeaderEl.hide();
14897 if(this.modalTitle.length){
14898 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14899 this.touchViewHeaderEl.show();
14902 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
14903 this.touchViewEl.show();
14905 this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
14906 this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14907 Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14909 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14911 if(this.modalTitle.length){
14912 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14915 this.touchViewBodyEl.setHeight(bodyHeight);
14919 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14921 this.touchViewEl.addClass('in');
14924 this.doTouchViewQuery();
14928 hideTouchView : function()
14930 this.touchViewEl.removeClass('in');
14934 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14936 this.touchViewEl.setStyle('display', 'none');
14941 setTouchViewValue : function()
14948 Roo.each(this.tickItems, function(o){
14953 this.hideTouchView();
14956 doTouchViewQuery : function()
14965 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
14969 if(!this.alwaysQuery || this.mode == 'local'){
14970 this.onTouchViewLoad();
14977 onTouchViewBeforeLoad : function(combo,opts)
14983 onTouchViewLoad : function()
14985 if(this.store.getCount() < 1){
14986 this.onTouchViewEmptyResults();
14990 this.clearTouchView();
14992 var rawValue = this.getRawValue();
14994 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
14996 this.tickItems = [];
14998 this.store.data.each(function(d, rowIndex){
14999 var row = this.touchViewListGroup.createChild(template);
15001 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15002 row.addClass(d.data.cls);
15005 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15008 html : d.data[this.displayField]
15011 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15012 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15015 row.removeClass('selected');
15016 if(!this.multiple && this.valueField &&
15017 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15020 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15021 row.addClass('selected');
15024 if(this.multiple && this.valueField &&
15025 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15029 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15030 this.tickItems.push(d.data);
15033 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15037 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15039 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15041 if(this.modalTitle.length){
15042 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15045 var listHeight = this.touchViewListGroup.getHeight();
15049 if(firstChecked && listHeight > bodyHeight){
15050 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15055 onTouchViewLoadException : function()
15057 this.hideTouchView();
15060 onTouchViewEmptyResults : function()
15062 this.clearTouchView();
15064 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15066 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15070 clearTouchView : function()
15072 this.touchViewListGroup.dom.innerHTML = '';
15075 onTouchViewClick : function(e, el, o)
15077 e.preventDefault();
15080 var rowIndex = o.rowIndex;
15082 var r = this.store.getAt(rowIndex);
15084 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15086 if(!this.multiple){
15087 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15088 c.dom.removeAttribute('checked');
15091 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15093 this.setFromData(r.data);
15095 var close = this.closeTriggerEl();
15101 this.hideTouchView();
15103 this.fireEvent('select', this, r, rowIndex);
15108 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15109 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15110 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15114 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15115 this.addItem(r.data);
15116 this.tickItems.push(r.data);
15120 getAutoCreateNativeIOS : function()
15123 cls: 'form-group' //input-group,
15128 cls : 'roo-ios-select'
15132 combobox.name = this.name;
15135 if (this.disabled) {
15136 combobox.disabled = true;
15139 var settings = this;
15141 ['xs','sm','md','lg'].map(function(size){
15142 if (settings[size]) {
15143 cfg.cls += ' col-' + size + '-' + settings[size];
15153 initIOSView : function()
15155 this.store.on('load', this.onIOSViewLoad, this);
15160 onIOSViewLoad : function()
15162 if(this.store.getCount() < 1){
15166 this.clearIOSView();
15168 if(this.allowBlank) {
15170 var default_text = '-- SELECT --';
15172 var opt = this.inputEl().createChild({
15175 html : default_text
15179 o[this.valueField] = 0;
15180 o[this.displayField] = default_text;
15182 this.ios_options.push({
15189 this.store.data.each(function(d, rowIndex){
15193 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15194 html = d.data[this.displayField];
15199 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15200 value = d.data[this.valueField];
15209 if(this.value == d.data[this.valueField]){
15210 option['selected'] = true;
15213 var opt = this.inputEl().createChild(option);
15215 this.ios_options.push({
15222 this.inputEl().on('change', function(){
15223 this.fireEvent('select', this);
15228 clearIOSView: function()
15230 this.inputEl().dom.innerHTML = '';
15232 this.ios_options = [];
15235 setIOSValue: function(v)
15239 if(!this.ios_options){
15243 Roo.each(this.ios_options, function(opts){
15245 opts.el.dom.removeAttribute('selected');
15247 if(opts.data[this.valueField] != v){
15251 opts.el.dom.setAttribute('selected', true);
15257 * @cfg {Boolean} grow
15261 * @cfg {Number} growMin
15265 * @cfg {Number} growMax
15274 Roo.apply(Roo.bootstrap.ComboBox, {
15278 cls: 'modal-header',
15300 cls: 'list-group-item',
15304 cls: 'roo-combobox-list-group-item-value'
15308 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15322 listItemCheckbox : {
15324 cls: 'list-group-item',
15328 cls: 'roo-combobox-list-group-item-value'
15332 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15348 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15353 cls: 'modal-footer',
15361 cls: 'col-xs-6 text-left',
15364 cls: 'btn btn-danger roo-touch-view-cancel',
15370 cls: 'col-xs-6 text-right',
15373 cls: 'btn btn-success roo-touch-view-ok',
15384 Roo.apply(Roo.bootstrap.ComboBox, {
15386 touchViewTemplate : {
15388 cls: 'modal fade roo-combobox-touch-view',
15392 cls: 'modal-dialog',
15393 style : 'position:fixed', // we have to fix position....
15397 cls: 'modal-content',
15399 Roo.bootstrap.ComboBox.header,
15400 Roo.bootstrap.ComboBox.body,
15401 Roo.bootstrap.ComboBox.footer
15410 * Ext JS Library 1.1.1
15411 * Copyright(c) 2006-2007, Ext JS, LLC.
15413 * Originally Released Under LGPL - original licence link has changed is not relivant.
15416 * <script type="text/javascript">
15421 * @extends Roo.util.Observable
15422 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
15423 * This class also supports single and multi selection modes. <br>
15424 * Create a data model bound view:
15426 var store = new Roo.data.Store(...);
15428 var view = new Roo.View({
15430 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
15432 singleSelect: true,
15433 selectedClass: "ydataview-selected",
15437 // listen for node click?
15438 view.on("click", function(vw, index, node, e){
15439 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15443 dataModel.load("foobar.xml");
15445 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15447 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15448 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15450 * Note: old style constructor is still suported (container, template, config)
15453 * Create a new View
15454 * @param {Object} config The config object
15457 Roo.View = function(config, depreciated_tpl, depreciated_config){
15459 this.parent = false;
15461 if (typeof(depreciated_tpl) == 'undefined') {
15462 // new way.. - universal constructor.
15463 Roo.apply(this, config);
15464 this.el = Roo.get(this.el);
15467 this.el = Roo.get(config);
15468 this.tpl = depreciated_tpl;
15469 Roo.apply(this, depreciated_config);
15471 this.wrapEl = this.el.wrap().wrap();
15472 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15475 if(typeof(this.tpl) == "string"){
15476 this.tpl = new Roo.Template(this.tpl);
15478 // support xtype ctors..
15479 this.tpl = new Roo.factory(this.tpl, Roo);
15483 this.tpl.compile();
15488 * @event beforeclick
15489 * Fires before a click is processed. Returns false to cancel the default action.
15490 * @param {Roo.View} this
15491 * @param {Number} index The index of the target node
15492 * @param {HTMLElement} node The target node
15493 * @param {Roo.EventObject} e The raw event object
15495 "beforeclick" : true,
15498 * Fires when a template node is clicked.
15499 * @param {Roo.View} this
15500 * @param {Number} index The index of the target node
15501 * @param {HTMLElement} node The target node
15502 * @param {Roo.EventObject} e The raw event object
15507 * Fires when a template node is double clicked.
15508 * @param {Roo.View} this
15509 * @param {Number} index The index of the target node
15510 * @param {HTMLElement} node The target node
15511 * @param {Roo.EventObject} e The raw event object
15515 * @event contextmenu
15516 * Fires when a template node is right clicked.
15517 * @param {Roo.View} this
15518 * @param {Number} index The index of the target node
15519 * @param {HTMLElement} node The target node
15520 * @param {Roo.EventObject} e The raw event object
15522 "contextmenu" : true,
15524 * @event selectionchange
15525 * Fires when the selected nodes change.
15526 * @param {Roo.View} this
15527 * @param {Array} selections Array of the selected nodes
15529 "selectionchange" : true,
15532 * @event beforeselect
15533 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15534 * @param {Roo.View} this
15535 * @param {HTMLElement} node The node to be selected
15536 * @param {Array} selections Array of currently selected nodes
15538 "beforeselect" : true,
15540 * @event preparedata
15541 * Fires on every row to render, to allow you to change the data.
15542 * @param {Roo.View} this
15543 * @param {Object} data to be rendered (change this)
15545 "preparedata" : true
15553 "click": this.onClick,
15554 "dblclick": this.onDblClick,
15555 "contextmenu": this.onContextMenu,
15559 this.selections = [];
15561 this.cmp = new Roo.CompositeElementLite([]);
15563 this.store = Roo.factory(this.store, Roo.data);
15564 this.setStore(this.store, true);
15567 if ( this.footer && this.footer.xtype) {
15569 var fctr = this.wrapEl.appendChild(document.createElement("div"));
15571 this.footer.dataSource = this.store;
15572 this.footer.container = fctr;
15573 this.footer = Roo.factory(this.footer, Roo);
15574 fctr.insertFirst(this.el);
15576 // this is a bit insane - as the paging toolbar seems to detach the el..
15577 // dom.parentNode.parentNode.parentNode
15578 // they get detached?
15582 Roo.View.superclass.constructor.call(this);
15587 Roo.extend(Roo.View, Roo.util.Observable, {
15590 * @cfg {Roo.data.Store} store Data store to load data from.
15595 * @cfg {String|Roo.Element} el The container element.
15600 * @cfg {String|Roo.Template} tpl The template used by this View
15604 * @cfg {String} dataName the named area of the template to use as the data area
15605 * Works with domtemplates roo-name="name"
15609 * @cfg {String} selectedClass The css class to add to selected nodes
15611 selectedClass : "x-view-selected",
15613 * @cfg {String} emptyText The empty text to show when nothing is loaded.
15618 * @cfg {String} text to display on mask (default Loading)
15622 * @cfg {Boolean} multiSelect Allow multiple selection
15624 multiSelect : false,
15626 * @cfg {Boolean} singleSelect Allow single selection
15628 singleSelect: false,
15631 * @cfg {Boolean} toggleSelect - selecting
15633 toggleSelect : false,
15636 * @cfg {Boolean} tickable - selecting
15641 * Returns the element this view is bound to.
15642 * @return {Roo.Element}
15644 getEl : function(){
15645 return this.wrapEl;
15651 * Refreshes the view. - called by datachanged on the store. - do not call directly.
15653 refresh : function(){
15654 //Roo.log('refresh');
15657 // if we are using something like 'domtemplate', then
15658 // the what gets used is:
15659 // t.applySubtemplate(NAME, data, wrapping data..)
15660 // the outer template then get' applied with
15661 // the store 'extra data'
15662 // and the body get's added to the
15663 // roo-name="data" node?
15664 // <span class='roo-tpl-{name}'></span> ?????
15668 this.clearSelections();
15669 this.el.update("");
15671 var records = this.store.getRange();
15672 if(records.length < 1) {
15674 // is this valid?? = should it render a template??
15676 this.el.update(this.emptyText);
15680 if (this.dataName) {
15681 this.el.update(t.apply(this.store.meta)); //????
15682 el = this.el.child('.roo-tpl-' + this.dataName);
15685 for(var i = 0, len = records.length; i < len; i++){
15686 var data = this.prepareData(records[i].data, i, records[i]);
15687 this.fireEvent("preparedata", this, data, i, records[i]);
15689 var d = Roo.apply({}, data);
15692 Roo.apply(d, {'roo-id' : Roo.id()});
15696 Roo.each(this.parent.item, function(item){
15697 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15700 Roo.apply(d, {'roo-data-checked' : 'checked'});
15704 html[html.length] = Roo.util.Format.trim(
15706 t.applySubtemplate(this.dataName, d, this.store.meta) :
15713 el.update(html.join(""));
15714 this.nodes = el.dom.childNodes;
15715 this.updateIndexes(0);
15720 * Function to override to reformat the data that is sent to
15721 * the template for each node.
15722 * DEPRICATED - use the preparedata event handler.
15723 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15724 * a JSON object for an UpdateManager bound view).
15726 prepareData : function(data, index, record)
15728 this.fireEvent("preparedata", this, data, index, record);
15732 onUpdate : function(ds, record){
15733 // Roo.log('on update');
15734 this.clearSelections();
15735 var index = this.store.indexOf(record);
15736 var n = this.nodes[index];
15737 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
15738 n.parentNode.removeChild(n);
15739 this.updateIndexes(index, index);
15745 onAdd : function(ds, records, index)
15747 //Roo.log(['on Add', ds, records, index] );
15748 this.clearSelections();
15749 if(this.nodes.length == 0){
15753 var n = this.nodes[index];
15754 for(var i = 0, len = records.length; i < len; i++){
15755 var d = this.prepareData(records[i].data, i, records[i]);
15757 this.tpl.insertBefore(n, d);
15760 this.tpl.append(this.el, d);
15763 this.updateIndexes(index);
15766 onRemove : function(ds, record, index){
15767 // Roo.log('onRemove');
15768 this.clearSelections();
15769 var el = this.dataName ?
15770 this.el.child('.roo-tpl-' + this.dataName) :
15773 el.dom.removeChild(this.nodes[index]);
15774 this.updateIndexes(index);
15778 * Refresh an individual node.
15779 * @param {Number} index
15781 refreshNode : function(index){
15782 this.onUpdate(this.store, this.store.getAt(index));
15785 updateIndexes : function(startIndex, endIndex){
15786 var ns = this.nodes;
15787 startIndex = startIndex || 0;
15788 endIndex = endIndex || ns.length - 1;
15789 for(var i = startIndex; i <= endIndex; i++){
15790 ns[i].nodeIndex = i;
15795 * Changes the data store this view uses and refresh the view.
15796 * @param {Store} store
15798 setStore : function(store, initial){
15799 if(!initial && this.store){
15800 this.store.un("datachanged", this.refresh);
15801 this.store.un("add", this.onAdd);
15802 this.store.un("remove", this.onRemove);
15803 this.store.un("update", this.onUpdate);
15804 this.store.un("clear", this.refresh);
15805 this.store.un("beforeload", this.onBeforeLoad);
15806 this.store.un("load", this.onLoad);
15807 this.store.un("loadexception", this.onLoad);
15811 store.on("datachanged", this.refresh, this);
15812 store.on("add", this.onAdd, this);
15813 store.on("remove", this.onRemove, this);
15814 store.on("update", this.onUpdate, this);
15815 store.on("clear", this.refresh, this);
15816 store.on("beforeload", this.onBeforeLoad, this);
15817 store.on("load", this.onLoad, this);
15818 store.on("loadexception", this.onLoad, this);
15826 * onbeforeLoad - masks the loading area.
15829 onBeforeLoad : function(store,opts)
15831 //Roo.log('onBeforeLoad');
15833 this.el.update("");
15835 this.el.mask(this.mask ? this.mask : "Loading" );
15837 onLoad : function ()
15844 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15845 * @param {HTMLElement} node
15846 * @return {HTMLElement} The template node
15848 findItemFromChild : function(node){
15849 var el = this.dataName ?
15850 this.el.child('.roo-tpl-' + this.dataName,true) :
15853 if(!node || node.parentNode == el){
15856 var p = node.parentNode;
15857 while(p && p != el){
15858 if(p.parentNode == el){
15867 onClick : function(e){
15868 var item = this.findItemFromChild(e.getTarget());
15870 var index = this.indexOf(item);
15871 if(this.onItemClick(item, index, e) !== false){
15872 this.fireEvent("click", this, index, item, e);
15875 this.clearSelections();
15880 onContextMenu : function(e){
15881 var item = this.findItemFromChild(e.getTarget());
15883 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15888 onDblClick : function(e){
15889 var item = this.findItemFromChild(e.getTarget());
15891 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15895 onItemClick : function(item, index, e)
15897 if(this.fireEvent("beforeclick", this, index, item, e) === false){
15900 if (this.toggleSelect) {
15901 var m = this.isSelected(item) ? 'unselect' : 'select';
15904 _t[m](item, true, false);
15907 if(this.multiSelect || this.singleSelect){
15908 if(this.multiSelect && e.shiftKey && this.lastSelection){
15909 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
15911 this.select(item, this.multiSelect && e.ctrlKey);
15912 this.lastSelection = item;
15915 if(!this.tickable){
15916 e.preventDefault();
15924 * Get the number of selected nodes.
15927 getSelectionCount : function(){
15928 return this.selections.length;
15932 * Get the currently selected nodes.
15933 * @return {Array} An array of HTMLElements
15935 getSelectedNodes : function(){
15936 return this.selections;
15940 * Get the indexes of the selected nodes.
15943 getSelectedIndexes : function(){
15944 var indexes = [], s = this.selections;
15945 for(var i = 0, len = s.length; i < len; i++){
15946 indexes.push(s[i].nodeIndex);
15952 * Clear all selections
15953 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
15955 clearSelections : function(suppressEvent){
15956 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
15957 this.cmp.elements = this.selections;
15958 this.cmp.removeClass(this.selectedClass);
15959 this.selections = [];
15960 if(!suppressEvent){
15961 this.fireEvent("selectionchange", this, this.selections);
15967 * Returns true if the passed node is selected
15968 * @param {HTMLElement/Number} node The node or node index
15969 * @return {Boolean}
15971 isSelected : function(node){
15972 var s = this.selections;
15976 node = this.getNode(node);
15977 return s.indexOf(node) !== -1;
15982 * @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
15983 * @param {Boolean} keepExisting (optional) true to keep existing selections
15984 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15986 select : function(nodeInfo, keepExisting, suppressEvent){
15987 if(nodeInfo instanceof Array){
15989 this.clearSelections(true);
15991 for(var i = 0, len = nodeInfo.length; i < len; i++){
15992 this.select(nodeInfo[i], true, true);
15996 var node = this.getNode(nodeInfo);
15997 if(!node || this.isSelected(node)){
15998 return; // already selected.
16001 this.clearSelections(true);
16004 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16005 Roo.fly(node).addClass(this.selectedClass);
16006 this.selections.push(node);
16007 if(!suppressEvent){
16008 this.fireEvent("selectionchange", this, this.selections);
16016 * @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
16017 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16018 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16020 unselect : function(nodeInfo, keepExisting, suppressEvent)
16022 if(nodeInfo instanceof Array){
16023 Roo.each(this.selections, function(s) {
16024 this.unselect(s, nodeInfo);
16028 var node = this.getNode(nodeInfo);
16029 if(!node || !this.isSelected(node)){
16030 //Roo.log("not selected");
16031 return; // not selected.
16035 Roo.each(this.selections, function(s) {
16037 Roo.fly(node).removeClass(this.selectedClass);
16044 this.selections= ns;
16045 this.fireEvent("selectionchange", this, this.selections);
16049 * Gets a template node.
16050 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16051 * @return {HTMLElement} The node or null if it wasn't found
16053 getNode : function(nodeInfo){
16054 if(typeof nodeInfo == "string"){
16055 return document.getElementById(nodeInfo);
16056 }else if(typeof nodeInfo == "number"){
16057 return this.nodes[nodeInfo];
16063 * Gets a range template nodes.
16064 * @param {Number} startIndex
16065 * @param {Number} endIndex
16066 * @return {Array} An array of nodes
16068 getNodes : function(start, end){
16069 var ns = this.nodes;
16070 start = start || 0;
16071 end = typeof end == "undefined" ? ns.length - 1 : end;
16074 for(var i = start; i <= end; i++){
16078 for(var i = start; i >= end; i--){
16086 * Finds the index of the passed node
16087 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16088 * @return {Number} The index of the node or -1
16090 indexOf : function(node){
16091 node = this.getNode(node);
16092 if(typeof node.nodeIndex == "number"){
16093 return node.nodeIndex;
16095 var ns = this.nodes;
16096 for(var i = 0, len = ns.length; i < len; i++){
16107 * based on jquery fullcalendar
16111 Roo.bootstrap = Roo.bootstrap || {};
16113 * @class Roo.bootstrap.Calendar
16114 * @extends Roo.bootstrap.Component
16115 * Bootstrap Calendar class
16116 * @cfg {Boolean} loadMask (true|false) default false
16117 * @cfg {Object} header generate the user specific header of the calendar, default false
16120 * Create a new Container
16121 * @param {Object} config The config object
16126 Roo.bootstrap.Calendar = function(config){
16127 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16131 * Fires when a date is selected
16132 * @param {DatePicker} this
16133 * @param {Date} date The selected date
16137 * @event monthchange
16138 * Fires when the displayed month changes
16139 * @param {DatePicker} this
16140 * @param {Date} date The selected month
16142 'monthchange': true,
16144 * @event evententer
16145 * Fires when mouse over an event
16146 * @param {Calendar} this
16147 * @param {event} Event
16149 'evententer': true,
16151 * @event eventleave
16152 * Fires when the mouse leaves an
16153 * @param {Calendar} this
16156 'eventleave': true,
16158 * @event eventclick
16159 * Fires when the mouse click an
16160 * @param {Calendar} this
16169 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
16172 * @cfg {Number} startDay
16173 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16181 getAutoCreate : function(){
16184 var fc_button = function(name, corner, style, content ) {
16185 return Roo.apply({},{
16187 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
16189 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16192 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16203 style : 'width:100%',
16210 cls : 'fc-header-left',
16212 fc_button('prev', 'left', 'arrow', '‹' ),
16213 fc_button('next', 'right', 'arrow', '›' ),
16214 { tag: 'span', cls: 'fc-header-space' },
16215 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
16223 cls : 'fc-header-center',
16227 cls: 'fc-header-title',
16230 html : 'month / year'
16238 cls : 'fc-header-right',
16240 /* fc_button('month', 'left', '', 'month' ),
16241 fc_button('week', '', '', 'week' ),
16242 fc_button('day', 'right', '', 'day' )
16254 header = this.header;
16257 var cal_heads = function() {
16259 // fixme - handle this.
16261 for (var i =0; i < Date.dayNames.length; i++) {
16262 var d = Date.dayNames[i];
16265 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16266 html : d.substring(0,3)
16270 ret[0].cls += ' fc-first';
16271 ret[6].cls += ' fc-last';
16274 var cal_cell = function(n) {
16277 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16282 cls: 'fc-day-number',
16286 cls: 'fc-day-content',
16290 style: 'position: relative;' // height: 17px;
16302 var cal_rows = function() {
16305 for (var r = 0; r < 6; r++) {
16312 for (var i =0; i < Date.dayNames.length; i++) {
16313 var d = Date.dayNames[i];
16314 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16317 row.cn[0].cls+=' fc-first';
16318 row.cn[0].cn[0].style = 'min-height:90px';
16319 row.cn[6].cls+=' fc-last';
16323 ret[0].cls += ' fc-first';
16324 ret[4].cls += ' fc-prev-last';
16325 ret[5].cls += ' fc-last';
16332 cls: 'fc-border-separate',
16333 style : 'width:100%',
16341 cls : 'fc-first fc-last',
16359 cls : 'fc-content',
16360 style : "position: relative;",
16363 cls : 'fc-view fc-view-month fc-grid',
16364 style : 'position: relative',
16365 unselectable : 'on',
16368 cls : 'fc-event-container',
16369 style : 'position:absolute;z-index:8;top:0;left:0;'
16387 initEvents : function()
16390 throw "can not find store for calendar";
16396 style: "text-align:center",
16400 style: "background-color:white;width:50%;margin:250 auto",
16404 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
16415 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16417 var size = this.el.select('.fc-content', true).first().getSize();
16418 this.maskEl.setSize(size.width, size.height);
16419 this.maskEl.enableDisplayMode("block");
16420 if(!this.loadMask){
16421 this.maskEl.hide();
16424 this.store = Roo.factory(this.store, Roo.data);
16425 this.store.on('load', this.onLoad, this);
16426 this.store.on('beforeload', this.onBeforeLoad, this);
16430 this.cells = this.el.select('.fc-day',true);
16431 //Roo.log(this.cells);
16432 this.textNodes = this.el.query('.fc-day-number');
16433 this.cells.addClassOnOver('fc-state-hover');
16435 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16436 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16437 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16438 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16440 this.on('monthchange', this.onMonthChange, this);
16442 this.update(new Date().clearTime());
16445 resize : function() {
16446 var sz = this.el.getSize();
16448 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16449 this.el.select('.fc-day-content div',true).setHeight(34);
16454 showPrevMonth : function(e){
16455 this.update(this.activeDate.add("mo", -1));
16457 showToday : function(e){
16458 this.update(new Date().clearTime());
16461 showNextMonth : function(e){
16462 this.update(this.activeDate.add("mo", 1));
16466 showPrevYear : function(){
16467 this.update(this.activeDate.add("y", -1));
16471 showNextYear : function(){
16472 this.update(this.activeDate.add("y", 1));
16477 update : function(date)
16479 var vd = this.activeDate;
16480 this.activeDate = date;
16481 // if(vd && this.el){
16482 // var t = date.getTime();
16483 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16484 // Roo.log('using add remove');
16486 // this.fireEvent('monthchange', this, date);
16488 // this.cells.removeClass("fc-state-highlight");
16489 // this.cells.each(function(c){
16490 // if(c.dateValue == t){
16491 // c.addClass("fc-state-highlight");
16492 // setTimeout(function(){
16493 // try{c.dom.firstChild.focus();}catch(e){}
16503 var days = date.getDaysInMonth();
16505 var firstOfMonth = date.getFirstDateOfMonth();
16506 var startingPos = firstOfMonth.getDay()-this.startDay;
16508 if(startingPos < this.startDay){
16512 var pm = date.add(Date.MONTH, -1);
16513 var prevStart = pm.getDaysInMonth()-startingPos;
16515 this.cells = this.el.select('.fc-day',true);
16516 this.textNodes = this.el.query('.fc-day-number');
16517 this.cells.addClassOnOver('fc-state-hover');
16519 var cells = this.cells.elements;
16520 var textEls = this.textNodes;
16522 Roo.each(cells, function(cell){
16523 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16526 days += startingPos;
16528 // convert everything to numbers so it's fast
16529 var day = 86400000;
16530 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16533 //Roo.log(prevStart);
16535 var today = new Date().clearTime().getTime();
16536 var sel = date.clearTime().getTime();
16537 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16538 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16539 var ddMatch = this.disabledDatesRE;
16540 var ddText = this.disabledDatesText;
16541 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16542 var ddaysText = this.disabledDaysText;
16543 var format = this.format;
16545 var setCellClass = function(cal, cell){
16549 //Roo.log('set Cell Class');
16551 var t = d.getTime();
16555 cell.dateValue = t;
16557 cell.className += " fc-today";
16558 cell.className += " fc-state-highlight";
16559 cell.title = cal.todayText;
16562 // disable highlight in other month..
16563 //cell.className += " fc-state-highlight";
16568 cell.className = " fc-state-disabled";
16569 cell.title = cal.minText;
16573 cell.className = " fc-state-disabled";
16574 cell.title = cal.maxText;
16578 if(ddays.indexOf(d.getDay()) != -1){
16579 cell.title = ddaysText;
16580 cell.className = " fc-state-disabled";
16583 if(ddMatch && format){
16584 var fvalue = d.dateFormat(format);
16585 if(ddMatch.test(fvalue)){
16586 cell.title = ddText.replace("%0", fvalue);
16587 cell.className = " fc-state-disabled";
16591 if (!cell.initialClassName) {
16592 cell.initialClassName = cell.dom.className;
16595 cell.dom.className = cell.initialClassName + ' ' + cell.className;
16600 for(; i < startingPos; i++) {
16601 textEls[i].innerHTML = (++prevStart);
16602 d.setDate(d.getDate()+1);
16604 cells[i].className = "fc-past fc-other-month";
16605 setCellClass(this, cells[i]);
16610 for(; i < days; i++){
16611 intDay = i - startingPos + 1;
16612 textEls[i].innerHTML = (intDay);
16613 d.setDate(d.getDate()+1);
16615 cells[i].className = ''; // "x-date-active";
16616 setCellClass(this, cells[i]);
16620 for(; i < 42; i++) {
16621 textEls[i].innerHTML = (++extraDays);
16622 d.setDate(d.getDate()+1);
16624 cells[i].className = "fc-future fc-other-month";
16625 setCellClass(this, cells[i]);
16628 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16630 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16632 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16633 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16635 if(totalRows != 6){
16636 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16637 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16640 this.fireEvent('monthchange', this, date);
16644 if(!this.internalRender){
16645 var main = this.el.dom.firstChild;
16646 var w = main.offsetWidth;
16647 this.el.setWidth(w + this.el.getBorderWidth("lr"));
16648 Roo.fly(main).setWidth(w);
16649 this.internalRender = true;
16650 // opera does not respect the auto grow header center column
16651 // then, after it gets a width opera refuses to recalculate
16652 // without a second pass
16653 if(Roo.isOpera && !this.secondPass){
16654 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16655 this.secondPass = true;
16656 this.update.defer(10, this, [date]);
16663 findCell : function(dt) {
16664 dt = dt.clearTime().getTime();
16666 this.cells.each(function(c){
16667 //Roo.log("check " +c.dateValue + '?=' + dt);
16668 if(c.dateValue == dt){
16678 findCells : function(ev) {
16679 var s = ev.start.clone().clearTime().getTime();
16681 var e= ev.end.clone().clearTime().getTime();
16684 this.cells.each(function(c){
16685 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16687 if(c.dateValue > e){
16690 if(c.dateValue < s){
16699 // findBestRow: function(cells)
16703 // for (var i =0 ; i < cells.length;i++) {
16704 // ret = Math.max(cells[i].rows || 0,ret);
16711 addItem : function(ev)
16713 // look for vertical location slot in
16714 var cells = this.findCells(ev);
16716 // ev.row = this.findBestRow(cells);
16718 // work out the location.
16722 for(var i =0; i < cells.length; i++) {
16724 cells[i].row = cells[0].row;
16727 cells[i].row = cells[i].row + 1;
16737 if (crow.start.getY() == cells[i].getY()) {
16739 crow.end = cells[i];
16756 cells[0].events.push(ev);
16758 this.calevents.push(ev);
16761 clearEvents: function() {
16763 if(!this.calevents){
16767 Roo.each(this.cells.elements, function(c){
16773 Roo.each(this.calevents, function(e) {
16774 Roo.each(e.els, function(el) {
16775 el.un('mouseenter' ,this.onEventEnter, this);
16776 el.un('mouseleave' ,this.onEventLeave, this);
16781 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16787 renderEvents: function()
16791 this.cells.each(function(c) {
16800 if(c.row != c.events.length){
16801 r = 4 - (4 - (c.row - c.events.length));
16804 c.events = ev.slice(0, r);
16805 c.more = ev.slice(r);
16807 if(c.more.length && c.more.length == 1){
16808 c.events.push(c.more.pop());
16811 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16815 this.cells.each(function(c) {
16817 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16820 for (var e = 0; e < c.events.length; e++){
16821 var ev = c.events[e];
16822 var rows = ev.rows;
16824 for(var i = 0; i < rows.length; i++) {
16826 // how many rows should it span..
16829 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16830 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16832 unselectable : "on",
16835 cls: 'fc-event-inner',
16839 // cls: 'fc-event-time',
16840 // html : cells.length > 1 ? '' : ev.time
16844 cls: 'fc-event-title',
16845 html : String.format('{0}', ev.title)
16852 cls: 'ui-resizable-handle ui-resizable-e',
16853 html : '  '
16860 cfg.cls += ' fc-event-start';
16862 if ((i+1) == rows.length) {
16863 cfg.cls += ' fc-event-end';
16866 var ctr = _this.el.select('.fc-event-container',true).first();
16867 var cg = ctr.createChild(cfg);
16869 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16870 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16872 var r = (c.more.length) ? 1 : 0;
16873 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
16874 cg.setWidth(ebox.right - sbox.x -2);
16876 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16877 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16878 cg.on('click', _this.onEventClick, _this, ev);
16889 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16890 style : 'position: absolute',
16891 unselectable : "on",
16894 cls: 'fc-event-inner',
16898 cls: 'fc-event-title',
16906 cls: 'ui-resizable-handle ui-resizable-e',
16907 html : '  '
16913 var ctr = _this.el.select('.fc-event-container',true).first();
16914 var cg = ctr.createChild(cfg);
16916 var sbox = c.select('.fc-day-content',true).first().getBox();
16917 var ebox = c.select('.fc-day-content',true).first().getBox();
16919 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
16920 cg.setWidth(ebox.right - sbox.x -2);
16922 cg.on('click', _this.onMoreEventClick, _this, c.more);
16932 onEventEnter: function (e, el,event,d) {
16933 this.fireEvent('evententer', this, el, event);
16936 onEventLeave: function (e, el,event,d) {
16937 this.fireEvent('eventleave', this, el, event);
16940 onEventClick: function (e, el,event,d) {
16941 this.fireEvent('eventclick', this, el, event);
16944 onMonthChange: function () {
16948 onMoreEventClick: function(e, el, more)
16952 this.calpopover.placement = 'right';
16953 this.calpopover.setTitle('More');
16955 this.calpopover.setContent('');
16957 var ctr = this.calpopover.el.select('.popover-content', true).first();
16959 Roo.each(more, function(m){
16961 cls : 'fc-event-hori fc-event-draggable',
16964 var cg = ctr.createChild(cfg);
16966 cg.on('click', _this.onEventClick, _this, m);
16969 this.calpopover.show(el);
16974 onLoad: function ()
16976 this.calevents = [];
16979 if(this.store.getCount() > 0){
16980 this.store.data.each(function(d){
16983 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
16984 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
16985 time : d.data.start_time,
16986 title : d.data.title,
16987 description : d.data.description,
16988 venue : d.data.venue
16993 this.renderEvents();
16995 if(this.calevents.length && this.loadMask){
16996 this.maskEl.hide();
17000 onBeforeLoad: function()
17002 this.clearEvents();
17004 this.maskEl.show();
17018 * @class Roo.bootstrap.Popover
17019 * @extends Roo.bootstrap.Component
17020 * Bootstrap Popover class
17021 * @cfg {String} html contents of the popover (or false to use children..)
17022 * @cfg {String} title of popover (or false to hide)
17023 * @cfg {String} placement how it is placed
17024 * @cfg {String} trigger click || hover (or false to trigger manually)
17025 * @cfg {String} over what (parent or false to trigger manually.)
17026 * @cfg {Number} delay - delay before showing
17029 * Create a new Popover
17030 * @param {Object} config The config object
17033 Roo.bootstrap.Popover = function(config){
17034 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17040 * After the popover show
17042 * @param {Roo.bootstrap.Popover} this
17047 * After the popover hide
17049 * @param {Roo.bootstrap.Popover} this
17055 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
17057 title: 'Fill in a title',
17060 placement : 'right',
17061 trigger : 'hover', // hover
17067 can_build_overlaid : false,
17069 getChildContainer : function()
17071 return this.el.select('.popover-content',true).first();
17074 getAutoCreate : function(){
17077 cls : 'popover roo-dynamic',
17078 style: 'display:block',
17084 cls : 'popover-inner',
17088 cls: 'popover-title',
17092 cls : 'popover-content',
17103 setTitle: function(str)
17106 this.el.select('.popover-title',true).first().dom.innerHTML = str;
17108 setContent: function(str)
17111 this.el.select('.popover-content',true).first().dom.innerHTML = str;
17113 // as it get's added to the bottom of the page.
17114 onRender : function(ct, position)
17116 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17118 var cfg = Roo.apply({}, this.getAutoCreate());
17122 cfg.cls += ' ' + this.cls;
17125 cfg.style = this.style;
17127 //Roo.log("adding to ");
17128 this.el = Roo.get(document.body).createChild(cfg, position);
17129 // Roo.log(this.el);
17134 initEvents : function()
17136 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17137 this.el.enableDisplayMode('block');
17139 if (this.over === false) {
17142 if (this.triggers === false) {
17145 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17146 var triggers = this.trigger ? this.trigger.split(' ') : [];
17147 Roo.each(triggers, function(trigger) {
17149 if (trigger == 'click') {
17150 on_el.on('click', this.toggle, this);
17151 } else if (trigger != 'manual') {
17152 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
17153 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17155 on_el.on(eventIn ,this.enter, this);
17156 on_el.on(eventOut, this.leave, this);
17167 toggle : function () {
17168 this.hoverState == 'in' ? this.leave() : this.enter();
17171 enter : function () {
17173 clearTimeout(this.timeout);
17175 this.hoverState = 'in';
17177 if (!this.delay || !this.delay.show) {
17182 this.timeout = setTimeout(function () {
17183 if (_t.hoverState == 'in') {
17186 }, this.delay.show)
17189 leave : function() {
17190 clearTimeout(this.timeout);
17192 this.hoverState = 'out';
17194 if (!this.delay || !this.delay.hide) {
17199 this.timeout = setTimeout(function () {
17200 if (_t.hoverState == 'out') {
17203 }, this.delay.hide)
17206 show : function (on_el)
17209 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17213 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17214 if (this.html !== false) {
17215 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17217 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17218 if (!this.title.length) {
17219 this.el.select('.popover-title',true).hide();
17222 var placement = typeof this.placement == 'function' ?
17223 this.placement.call(this, this.el, on_el) :
17226 var autoToken = /\s?auto?\s?/i;
17227 var autoPlace = autoToken.test(placement);
17229 placement = placement.replace(autoToken, '') || 'top';
17233 //this.el.setXY([0,0]);
17235 this.el.dom.style.display='block';
17236 this.el.addClass(placement);
17238 //this.el.appendTo(on_el);
17240 var p = this.getPosition();
17241 var box = this.el.getBox();
17246 var align = Roo.bootstrap.Popover.alignment[placement];
17247 this.el.alignTo(on_el, align[0],align[1]);
17248 //var arrow = this.el.select('.arrow',true).first();
17249 //arrow.set(align[2],
17251 this.el.addClass('in');
17254 if (this.el.hasClass('fade')) {
17258 this.hoverState = 'in';
17260 this.fireEvent('show', this);
17265 this.el.setXY([0,0]);
17266 this.el.removeClass('in');
17268 this.hoverState = null;
17270 this.fireEvent('hide', this);
17275 Roo.bootstrap.Popover.alignment = {
17276 'left' : ['r-l', [-10,0], 'right'],
17277 'right' : ['l-r', [10,0], 'left'],
17278 'bottom' : ['t-b', [0,10], 'top'],
17279 'top' : [ 'b-t', [0,-10], 'bottom']
17290 * @class Roo.bootstrap.Progress
17291 * @extends Roo.bootstrap.Component
17292 * Bootstrap Progress class
17293 * @cfg {Boolean} striped striped of the progress bar
17294 * @cfg {Boolean} active animated of the progress bar
17298 * Create a new Progress
17299 * @param {Object} config The config object
17302 Roo.bootstrap.Progress = function(config){
17303 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17306 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
17311 getAutoCreate : function(){
17319 cfg.cls += ' progress-striped';
17323 cfg.cls += ' active';
17342 * @class Roo.bootstrap.ProgressBar
17343 * @extends Roo.bootstrap.Component
17344 * Bootstrap ProgressBar class
17345 * @cfg {Number} aria_valuenow aria-value now
17346 * @cfg {Number} aria_valuemin aria-value min
17347 * @cfg {Number} aria_valuemax aria-value max
17348 * @cfg {String} label label for the progress bar
17349 * @cfg {String} panel (success | info | warning | danger )
17350 * @cfg {String} role role of the progress bar
17351 * @cfg {String} sr_only text
17355 * Create a new ProgressBar
17356 * @param {Object} config The config object
17359 Roo.bootstrap.ProgressBar = function(config){
17360 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17363 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
17367 aria_valuemax : 100,
17373 getAutoCreate : function()
17378 cls: 'progress-bar',
17379 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17391 cfg.role = this.role;
17394 if(this.aria_valuenow){
17395 cfg['aria-valuenow'] = this.aria_valuenow;
17398 if(this.aria_valuemin){
17399 cfg['aria-valuemin'] = this.aria_valuemin;
17402 if(this.aria_valuemax){
17403 cfg['aria-valuemax'] = this.aria_valuemax;
17406 if(this.label && !this.sr_only){
17407 cfg.html = this.label;
17411 cfg.cls += ' progress-bar-' + this.panel;
17417 update : function(aria_valuenow)
17419 this.aria_valuenow = aria_valuenow;
17421 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17436 * @class Roo.bootstrap.TabGroup
17437 * @extends Roo.bootstrap.Column
17438 * Bootstrap Column class
17439 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17440 * @cfg {Boolean} carousel true to make the group behave like a carousel
17441 * @cfg {Boolean} bullets show bullets for the panels
17442 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17443 * @cfg {Number} timer auto slide timer .. default 0 millisecond
17444 * @cfg {Boolean} showarrow (true|false) show arrow default true
17447 * Create a new TabGroup
17448 * @param {Object} config The config object
17451 Roo.bootstrap.TabGroup = function(config){
17452 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17454 this.navId = Roo.id();
17457 Roo.bootstrap.TabGroup.register(this);
17461 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
17464 transition : false,
17469 slideOnTouch : false,
17472 getAutoCreate : function()
17474 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17476 cfg.cls += ' tab-content';
17478 if (this.carousel) {
17479 cfg.cls += ' carousel slide';
17482 cls : 'carousel-inner',
17486 if(this.bullets && !Roo.isTouch){
17489 cls : 'carousel-bullets',
17493 if(this.bullets_cls){
17494 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17501 cfg.cn[0].cn.push(bullets);
17504 if(this.showarrow){
17505 cfg.cn[0].cn.push({
17507 class : 'carousel-arrow',
17511 class : 'carousel-prev',
17515 class : 'fa fa-chevron-left'
17521 class : 'carousel-next',
17525 class : 'fa fa-chevron-right'
17538 initEvents: function()
17540 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17541 // this.el.on("touchstart", this.onTouchStart, this);
17544 if(this.autoslide){
17547 this.slideFn = window.setInterval(function() {
17548 _this.showPanelNext();
17552 if(this.showarrow){
17553 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17554 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17560 // onTouchStart : function(e, el, o)
17562 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17566 // this.showPanelNext();
17570 getChildContainer : function()
17572 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17576 * register a Navigation item
17577 * @param {Roo.bootstrap.NavItem} the navitem to add
17579 register : function(item)
17581 this.tabs.push( item);
17582 item.navId = this.navId; // not really needed..
17587 getActivePanel : function()
17590 Roo.each(this.tabs, function(t) {
17600 getPanelByName : function(n)
17603 Roo.each(this.tabs, function(t) {
17604 if (t.tabId == n) {
17612 indexOfPanel : function(p)
17615 Roo.each(this.tabs, function(t,i) {
17616 if (t.tabId == p.tabId) {
17625 * show a specific panel
17626 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17627 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17629 showPanel : function (pan)
17631 if(this.transition || typeof(pan) == 'undefined'){
17632 Roo.log("waiting for the transitionend");
17636 if (typeof(pan) == 'number') {
17637 pan = this.tabs[pan];
17640 if (typeof(pan) == 'string') {
17641 pan = this.getPanelByName(pan);
17644 var cur = this.getActivePanel();
17647 Roo.log('pan or acitve pan is undefined');
17651 if (pan.tabId == this.getActivePanel().tabId) {
17655 if (false === cur.fireEvent('beforedeactivate')) {
17659 if(this.bullets > 0 && !Roo.isTouch){
17660 this.setActiveBullet(this.indexOfPanel(pan));
17663 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17665 this.transition = true;
17666 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
17667 var lr = dir == 'next' ? 'left' : 'right';
17668 pan.el.addClass(dir); // or prev
17669 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17670 cur.el.addClass(lr); // or right
17671 pan.el.addClass(lr);
17674 cur.el.on('transitionend', function() {
17675 Roo.log("trans end?");
17677 pan.el.removeClass([lr,dir]);
17678 pan.setActive(true);
17680 cur.el.removeClass([lr]);
17681 cur.setActive(false);
17683 _this.transition = false;
17685 }, this, { single: true } );
17690 cur.setActive(false);
17691 pan.setActive(true);
17696 showPanelNext : function()
17698 var i = this.indexOfPanel(this.getActivePanel());
17700 if (i >= this.tabs.length - 1 && !this.autoslide) {
17704 if (i >= this.tabs.length - 1 && this.autoslide) {
17708 this.showPanel(this.tabs[i+1]);
17711 showPanelPrev : function()
17713 var i = this.indexOfPanel(this.getActivePanel());
17715 if (i < 1 && !this.autoslide) {
17719 if (i < 1 && this.autoslide) {
17720 i = this.tabs.length;
17723 this.showPanel(this.tabs[i-1]);
17727 addBullet: function()
17729 if(!this.bullets || Roo.isTouch){
17732 var ctr = this.el.select('.carousel-bullets',true).first();
17733 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
17734 var bullet = ctr.createChild({
17735 cls : 'bullet bullet-' + i
17736 },ctr.dom.lastChild);
17741 bullet.on('click', (function(e, el, o, ii, t){
17743 e.preventDefault();
17745 this.showPanel(ii);
17747 if(this.autoslide && this.slideFn){
17748 clearInterval(this.slideFn);
17749 this.slideFn = window.setInterval(function() {
17750 _this.showPanelNext();
17754 }).createDelegate(this, [i, bullet], true));
17759 setActiveBullet : function(i)
17765 Roo.each(this.el.select('.bullet', true).elements, function(el){
17766 el.removeClass('selected');
17769 var bullet = this.el.select('.bullet-' + i, true).first();
17775 bullet.addClass('selected');
17786 Roo.apply(Roo.bootstrap.TabGroup, {
17790 * register a Navigation Group
17791 * @param {Roo.bootstrap.NavGroup} the navgroup to add
17793 register : function(navgrp)
17795 this.groups[navgrp.navId] = navgrp;
17799 * fetch a Navigation Group based on the navigation ID
17800 * if one does not exist , it will get created.
17801 * @param {string} the navgroup to add
17802 * @returns {Roo.bootstrap.NavGroup} the navgroup
17804 get: function(navId) {
17805 if (typeof(this.groups[navId]) == 'undefined') {
17806 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17808 return this.groups[navId] ;
17823 * @class Roo.bootstrap.TabPanel
17824 * @extends Roo.bootstrap.Component
17825 * Bootstrap TabPanel class
17826 * @cfg {Boolean} active panel active
17827 * @cfg {String} html panel content
17828 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17829 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17830 * @cfg {String} href click to link..
17834 * Create a new TabPanel
17835 * @param {Object} config The config object
17838 Roo.bootstrap.TabPanel = function(config){
17839 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17843 * Fires when the active status changes
17844 * @param {Roo.bootstrap.TabPanel} this
17845 * @param {Boolean} state the new state
17850 * @event beforedeactivate
17851 * Fires before a tab is de-activated - can be used to do validation on a form.
17852 * @param {Roo.bootstrap.TabPanel} this
17853 * @return {Boolean} false if there is an error
17856 'beforedeactivate': true
17859 this.tabId = this.tabId || Roo.id();
17863 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
17871 getAutoCreate : function(){
17874 // item is needed for carousel - not sure if it has any effect otherwise
17875 cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17876 html: this.html || ''
17880 cfg.cls += ' active';
17884 cfg.tabId = this.tabId;
17891 initEvents: function()
17893 var p = this.parent();
17895 this.navId = this.navId || p.navId;
17897 if (typeof(this.navId) != 'undefined') {
17898 // not really needed.. but just in case.. parent should be a NavGroup.
17899 var tg = Roo.bootstrap.TabGroup.get(this.navId);
17903 var i = tg.tabs.length - 1;
17905 if(this.active && tg.bullets > 0 && i < tg.bullets){
17906 tg.setActiveBullet(i);
17910 this.el.on('click', this.onClick, this);
17913 this.el.on("touchstart", this.onTouchStart, this);
17914 this.el.on("touchmove", this.onTouchMove, this);
17915 this.el.on("touchend", this.onTouchEnd, this);
17920 onRender : function(ct, position)
17922 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
17925 setActive : function(state)
17927 Roo.log("panel - set active " + this.tabId + "=" + state);
17929 this.active = state;
17931 this.el.removeClass('active');
17933 } else if (!this.el.hasClass('active')) {
17934 this.el.addClass('active');
17937 this.fireEvent('changed', this, state);
17940 onClick : function(e)
17942 e.preventDefault();
17944 if(!this.href.length){
17948 window.location.href = this.href;
17957 onTouchStart : function(e)
17959 this.swiping = false;
17961 this.startX = e.browserEvent.touches[0].clientX;
17962 this.startY = e.browserEvent.touches[0].clientY;
17965 onTouchMove : function(e)
17967 this.swiping = true;
17969 this.endX = e.browserEvent.touches[0].clientX;
17970 this.endY = e.browserEvent.touches[0].clientY;
17973 onTouchEnd : function(e)
17980 var tabGroup = this.parent();
17982 if(this.endX > this.startX){ // swiping right
17983 tabGroup.showPanelPrev();
17987 if(this.startX > this.endX){ // swiping left
17988 tabGroup.showPanelNext();
18007 * @class Roo.bootstrap.DateField
18008 * @extends Roo.bootstrap.Input
18009 * Bootstrap DateField class
18010 * @cfg {Number} weekStart default 0
18011 * @cfg {String} viewMode default empty, (months|years)
18012 * @cfg {String} minViewMode default empty, (months|years)
18013 * @cfg {Number} startDate default -Infinity
18014 * @cfg {Number} endDate default Infinity
18015 * @cfg {Boolean} todayHighlight default false
18016 * @cfg {Boolean} todayBtn default false
18017 * @cfg {Boolean} calendarWeeks default false
18018 * @cfg {Object} daysOfWeekDisabled default empty
18019 * @cfg {Boolean} singleMode default false (true | false)
18021 * @cfg {Boolean} keyboardNavigation default true
18022 * @cfg {String} language default en
18025 * Create a new DateField
18026 * @param {Object} config The config object
18029 Roo.bootstrap.DateField = function(config){
18030 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18034 * Fires when this field show.
18035 * @param {Roo.bootstrap.DateField} this
18036 * @param {Mixed} date The date value
18041 * Fires when this field hide.
18042 * @param {Roo.bootstrap.DateField} this
18043 * @param {Mixed} date The date value
18048 * Fires when select a date.
18049 * @param {Roo.bootstrap.DateField} this
18050 * @param {Mixed} date The date value
18054 * @event beforeselect
18055 * Fires when before select a date.
18056 * @param {Roo.bootstrap.DateField} this
18057 * @param {Mixed} date The date value
18059 beforeselect : true
18063 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
18066 * @cfg {String} format
18067 * The default date format string which can be overriden for localization support. The format must be
18068 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18072 * @cfg {String} altFormats
18073 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18074 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18076 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18084 todayHighlight : false,
18090 keyboardNavigation: true,
18092 calendarWeeks: false,
18094 startDate: -Infinity,
18098 daysOfWeekDisabled: [],
18102 singleMode : false,
18104 UTCDate: function()
18106 return new Date(Date.UTC.apply(Date, arguments));
18109 UTCToday: function()
18111 var today = new Date();
18112 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18115 getDate: function() {
18116 var d = this.getUTCDate();
18117 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18120 getUTCDate: function() {
18124 setDate: function(d) {
18125 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18128 setUTCDate: function(d) {
18130 this.setValue(this.formatDate(this.date));
18133 onRender: function(ct, position)
18136 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18138 this.language = this.language || 'en';
18139 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18140 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18142 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18143 this.format = this.format || 'm/d/y';
18144 this.isInline = false;
18145 this.isInput = true;
18146 this.component = this.el.select('.add-on', true).first() || false;
18147 this.component = (this.component && this.component.length === 0) ? false : this.component;
18148 this.hasInput = this.component && this.inputEl().length;
18150 if (typeof(this.minViewMode === 'string')) {
18151 switch (this.minViewMode) {
18153 this.minViewMode = 1;
18156 this.minViewMode = 2;
18159 this.minViewMode = 0;
18164 if (typeof(this.viewMode === 'string')) {
18165 switch (this.viewMode) {
18178 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18180 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18182 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18184 this.picker().on('mousedown', this.onMousedown, this);
18185 this.picker().on('click', this.onClick, this);
18187 this.picker().addClass('datepicker-dropdown');
18189 this.startViewMode = this.viewMode;
18191 if(this.singleMode){
18192 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18193 v.setVisibilityMode(Roo.Element.DISPLAY);
18197 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18198 v.setStyle('width', '189px');
18202 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18203 if(!this.calendarWeeks){
18208 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18209 v.attr('colspan', function(i, val){
18210 return parseInt(val) + 1;
18215 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18217 this.setStartDate(this.startDate);
18218 this.setEndDate(this.endDate);
18220 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18227 if(this.isInline) {
18232 picker : function()
18234 return this.pickerEl;
18235 // return this.el.select('.datepicker', true).first();
18238 fillDow: function()
18240 var dowCnt = this.weekStart;
18249 if(this.calendarWeeks){
18257 while (dowCnt < this.weekStart + 7) {
18261 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18265 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18268 fillMonths: function()
18271 var months = this.picker().select('>.datepicker-months td', true).first();
18273 months.dom.innerHTML = '';
18279 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18282 months.createChild(month);
18289 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;
18291 if (this.date < this.startDate) {
18292 this.viewDate = new Date(this.startDate);
18293 } else if (this.date > this.endDate) {
18294 this.viewDate = new Date(this.endDate);
18296 this.viewDate = new Date(this.date);
18304 var d = new Date(this.viewDate),
18305 year = d.getUTCFullYear(),
18306 month = d.getUTCMonth(),
18307 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18308 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18309 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18310 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18311 currentDate = this.date && this.date.valueOf(),
18312 today = this.UTCToday();
18314 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18316 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18318 // this.picker.select('>tfoot th.today').
18319 // .text(dates[this.language].today)
18320 // .toggle(this.todayBtn !== false);
18322 this.updateNavArrows();
18325 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18327 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18329 prevMonth.setUTCDate(day);
18331 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18333 var nextMonth = new Date(prevMonth);
18335 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18337 nextMonth = nextMonth.valueOf();
18339 var fillMonths = false;
18341 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18343 while(prevMonth.valueOf() < nextMonth) {
18346 if (prevMonth.getUTCDay() === this.weekStart) {
18348 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18356 if(this.calendarWeeks){
18357 // ISO 8601: First week contains first thursday.
18358 // ISO also states week starts on Monday, but we can be more abstract here.
18360 // Start of current week: based on weekstart/current date
18361 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18362 // Thursday of this week
18363 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18364 // First Thursday of year, year from thursday
18365 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18366 // Calendar week: ms between thursdays, div ms per day, div 7 days
18367 calWeek = (th - yth) / 864e5 / 7 + 1;
18369 fillMonths.cn.push({
18377 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18379 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18382 if (this.todayHighlight &&
18383 prevMonth.getUTCFullYear() == today.getFullYear() &&
18384 prevMonth.getUTCMonth() == today.getMonth() &&
18385 prevMonth.getUTCDate() == today.getDate()) {
18386 clsName += ' today';
18389 if (currentDate && prevMonth.valueOf() === currentDate) {
18390 clsName += ' active';
18393 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18394 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18395 clsName += ' disabled';
18398 fillMonths.cn.push({
18400 cls: 'day ' + clsName,
18401 html: prevMonth.getDate()
18404 prevMonth.setDate(prevMonth.getDate()+1);
18407 var currentYear = this.date && this.date.getUTCFullYear();
18408 var currentMonth = this.date && this.date.getUTCMonth();
18410 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18412 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18413 v.removeClass('active');
18415 if(currentYear === year && k === currentMonth){
18416 v.addClass('active');
18419 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18420 v.addClass('disabled');
18426 year = parseInt(year/10, 10) * 10;
18428 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18430 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18433 for (var i = -1; i < 11; i++) {
18434 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18436 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18444 showMode: function(dir)
18447 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18450 Roo.each(this.picker().select('>div',true).elements, function(v){
18451 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18454 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18459 if(this.isInline) {
18463 this.picker().removeClass(['bottom', 'top']);
18465 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18467 * place to the top of element!
18471 this.picker().addClass('top');
18472 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18477 this.picker().addClass('bottom');
18479 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18482 parseDate : function(value)
18484 if(!value || value instanceof Date){
18487 var v = Date.parseDate(value, this.format);
18488 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18489 v = Date.parseDate(value, 'Y-m-d');
18491 if(!v && this.altFormats){
18492 if(!this.altFormatsArray){
18493 this.altFormatsArray = this.altFormats.split("|");
18495 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18496 v = Date.parseDate(value, this.altFormatsArray[i]);
18502 formatDate : function(date, fmt)
18504 return (!date || !(date instanceof Date)) ?
18505 date : date.dateFormat(fmt || this.format);
18508 onFocus : function()
18510 Roo.bootstrap.DateField.superclass.onFocus.call(this);
18514 onBlur : function()
18516 Roo.bootstrap.DateField.superclass.onBlur.call(this);
18518 var d = this.inputEl().getValue();
18527 this.picker().show();
18531 this.fireEvent('show', this, this.date);
18536 if(this.isInline) {
18539 this.picker().hide();
18540 this.viewMode = this.startViewMode;
18543 this.fireEvent('hide', this, this.date);
18547 onMousedown: function(e)
18549 e.stopPropagation();
18550 e.preventDefault();
18555 Roo.bootstrap.DateField.superclass.keyup.call(this);
18559 setValue: function(v)
18561 if(this.fireEvent('beforeselect', this, v) !== false){
18562 var d = new Date(this.parseDate(v) ).clearTime();
18564 if(isNaN(d.getTime())){
18565 this.date = this.viewDate = '';
18566 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18570 v = this.formatDate(d);
18572 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18574 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18578 this.fireEvent('select', this, this.date);
18582 getValue: function()
18584 return this.formatDate(this.date);
18587 fireKey: function(e)
18589 if (!this.picker().isVisible()){
18590 if (e.keyCode == 27) { // allow escape to hide and re-show picker
18596 var dateChanged = false,
18598 newDate, newViewDate;
18603 e.preventDefault();
18607 if (!this.keyboardNavigation) {
18610 dir = e.keyCode == 37 ? -1 : 1;
18613 newDate = this.moveYear(this.date, dir);
18614 newViewDate = this.moveYear(this.viewDate, dir);
18615 } else if (e.shiftKey){
18616 newDate = this.moveMonth(this.date, dir);
18617 newViewDate = this.moveMonth(this.viewDate, dir);
18619 newDate = new Date(this.date);
18620 newDate.setUTCDate(this.date.getUTCDate() + dir);
18621 newViewDate = new Date(this.viewDate);
18622 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18624 if (this.dateWithinRange(newDate)){
18625 this.date = newDate;
18626 this.viewDate = newViewDate;
18627 this.setValue(this.formatDate(this.date));
18629 e.preventDefault();
18630 dateChanged = true;
18635 if (!this.keyboardNavigation) {
18638 dir = e.keyCode == 38 ? -1 : 1;
18640 newDate = this.moveYear(this.date, dir);
18641 newViewDate = this.moveYear(this.viewDate, dir);
18642 } else if (e.shiftKey){
18643 newDate = this.moveMonth(this.date, dir);
18644 newViewDate = this.moveMonth(this.viewDate, dir);
18646 newDate = new Date(this.date);
18647 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18648 newViewDate = new Date(this.viewDate);
18649 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18651 if (this.dateWithinRange(newDate)){
18652 this.date = newDate;
18653 this.viewDate = newViewDate;
18654 this.setValue(this.formatDate(this.date));
18656 e.preventDefault();
18657 dateChanged = true;
18661 this.setValue(this.formatDate(this.date));
18663 e.preventDefault();
18666 this.setValue(this.formatDate(this.date));
18680 onClick: function(e)
18682 e.stopPropagation();
18683 e.preventDefault();
18685 var target = e.getTarget();
18687 if(target.nodeName.toLowerCase() === 'i'){
18688 target = Roo.get(target).dom.parentNode;
18691 var nodeName = target.nodeName;
18692 var className = target.className;
18693 var html = target.innerHTML;
18694 //Roo.log(nodeName);
18696 switch(nodeName.toLowerCase()) {
18698 switch(className) {
18704 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18705 switch(this.viewMode){
18707 this.viewDate = this.moveMonth(this.viewDate, dir);
18711 this.viewDate = this.moveYear(this.viewDate, dir);
18717 var date = new Date();
18718 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18720 this.setValue(this.formatDate(this.date));
18727 if (className.indexOf('disabled') < 0) {
18728 this.viewDate.setUTCDate(1);
18729 if (className.indexOf('month') > -1) {
18730 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18732 var year = parseInt(html, 10) || 0;
18733 this.viewDate.setUTCFullYear(year);
18737 if(this.singleMode){
18738 this.setValue(this.formatDate(this.viewDate));
18749 //Roo.log(className);
18750 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
18751 var day = parseInt(html, 10) || 1;
18752 var year = this.viewDate.getUTCFullYear(),
18753 month = this.viewDate.getUTCMonth();
18755 if (className.indexOf('old') > -1) {
18762 } else if (className.indexOf('new') > -1) {
18770 //Roo.log([year,month,day]);
18771 this.date = this.UTCDate(year, month, day,0,0,0,0);
18772 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
18774 //Roo.log(this.formatDate(this.date));
18775 this.setValue(this.formatDate(this.date));
18782 setStartDate: function(startDate)
18784 this.startDate = startDate || -Infinity;
18785 if (this.startDate !== -Infinity) {
18786 this.startDate = this.parseDate(this.startDate);
18789 this.updateNavArrows();
18792 setEndDate: function(endDate)
18794 this.endDate = endDate || Infinity;
18795 if (this.endDate !== Infinity) {
18796 this.endDate = this.parseDate(this.endDate);
18799 this.updateNavArrows();
18802 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18804 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18805 if (typeof(this.daysOfWeekDisabled) !== 'object') {
18806 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18808 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18809 return parseInt(d, 10);
18812 this.updateNavArrows();
18815 updateNavArrows: function()
18817 if(this.singleMode){
18821 var d = new Date(this.viewDate),
18822 year = d.getUTCFullYear(),
18823 month = d.getUTCMonth();
18825 Roo.each(this.picker().select('.prev', true).elements, function(v){
18827 switch (this.viewMode) {
18830 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18836 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18843 Roo.each(this.picker().select('.next', true).elements, function(v){
18845 switch (this.viewMode) {
18848 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18854 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18862 moveMonth: function(date, dir)
18867 var new_date = new Date(date.valueOf()),
18868 day = new_date.getUTCDate(),
18869 month = new_date.getUTCMonth(),
18870 mag = Math.abs(dir),
18872 dir = dir > 0 ? 1 : -1;
18875 // If going back one month, make sure month is not current month
18876 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18878 return new_date.getUTCMonth() == month;
18880 // If going forward one month, make sure month is as expected
18881 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18883 return new_date.getUTCMonth() != new_month;
18885 new_month = month + dir;
18886 new_date.setUTCMonth(new_month);
18887 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18888 if (new_month < 0 || new_month > 11) {
18889 new_month = (new_month + 12) % 12;
18892 // For magnitudes >1, move one month at a time...
18893 for (var i=0; i<mag; i++) {
18894 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18895 new_date = this.moveMonth(new_date, dir);
18897 // ...then reset the day, keeping it in the new month
18898 new_month = new_date.getUTCMonth();
18899 new_date.setUTCDate(day);
18901 return new_month != new_date.getUTCMonth();
18904 // Common date-resetting loop -- if date is beyond end of month, make it
18907 new_date.setUTCDate(--day);
18908 new_date.setUTCMonth(new_month);
18913 moveYear: function(date, dir)
18915 return this.moveMonth(date, dir*12);
18918 dateWithinRange: function(date)
18920 return date >= this.startDate && date <= this.endDate;
18926 this.picker().remove();
18929 validateValue : function(value)
18931 if(value.length < 1) {
18932 if(this.allowBlank){
18938 if(value.length < this.minLength){
18941 if(value.length > this.maxLength){
18945 var vt = Roo.form.VTypes;
18946 if(!vt[this.vtype](value, this)){
18950 if(typeof this.validator == "function"){
18951 var msg = this.validator(value);
18957 if(this.regex && !this.regex.test(value)){
18961 if(typeof(this.parseDate(value)) == 'undefined'){
18965 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
18969 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
18979 Roo.apply(Roo.bootstrap.DateField, {
18990 html: '<i class="fa fa-arrow-left"/>'
19000 html: '<i class="fa fa-arrow-right"/>'
19042 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19043 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19044 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19045 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19046 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19059 navFnc: 'FullYear',
19064 navFnc: 'FullYear',
19069 Roo.apply(Roo.bootstrap.DateField, {
19073 cls: 'datepicker dropdown-menu roo-dynamic',
19077 cls: 'datepicker-days',
19081 cls: 'table-condensed',
19083 Roo.bootstrap.DateField.head,
19087 Roo.bootstrap.DateField.footer
19094 cls: 'datepicker-months',
19098 cls: 'table-condensed',
19100 Roo.bootstrap.DateField.head,
19101 Roo.bootstrap.DateField.content,
19102 Roo.bootstrap.DateField.footer
19109 cls: 'datepicker-years',
19113 cls: 'table-condensed',
19115 Roo.bootstrap.DateField.head,
19116 Roo.bootstrap.DateField.content,
19117 Roo.bootstrap.DateField.footer
19136 * @class Roo.bootstrap.TimeField
19137 * @extends Roo.bootstrap.Input
19138 * Bootstrap DateField class
19142 * Create a new TimeField
19143 * @param {Object} config The config object
19146 Roo.bootstrap.TimeField = function(config){
19147 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19151 * Fires when this field show.
19152 * @param {Roo.bootstrap.DateField} thisthis
19153 * @param {Mixed} date The date value
19158 * Fires when this field hide.
19159 * @param {Roo.bootstrap.DateField} this
19160 * @param {Mixed} date The date value
19165 * Fires when select a date.
19166 * @param {Roo.bootstrap.DateField} this
19167 * @param {Mixed} date The date value
19173 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
19176 * @cfg {String} format
19177 * The default time format string which can be overriden for localization support. The format must be
19178 * valid according to {@link Date#parseDate} (defaults to 'H:i').
19182 onRender: function(ct, position)
19185 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19187 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19189 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19191 this.pop = this.picker().select('>.datepicker-time',true).first();
19192 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19194 this.picker().on('mousedown', this.onMousedown, this);
19195 this.picker().on('click', this.onClick, this);
19197 this.picker().addClass('datepicker-dropdown');
19202 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19203 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19204 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19205 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19206 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19207 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19211 fireKey: function(e){
19212 if (!this.picker().isVisible()){
19213 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19219 e.preventDefault();
19227 this.onTogglePeriod();
19230 this.onIncrementMinutes();
19233 this.onDecrementMinutes();
19242 onClick: function(e) {
19243 e.stopPropagation();
19244 e.preventDefault();
19247 picker : function()
19249 return this.el.select('.datepicker', true).first();
19252 fillTime: function()
19254 var time = this.pop.select('tbody', true).first();
19256 time.dom.innerHTML = '';
19271 cls: 'hours-up glyphicon glyphicon-chevron-up'
19291 cls: 'minutes-up glyphicon glyphicon-chevron-up'
19312 cls: 'timepicker-hour',
19327 cls: 'timepicker-minute',
19342 cls: 'btn btn-primary period',
19364 cls: 'hours-down glyphicon glyphicon-chevron-down'
19384 cls: 'minutes-down glyphicon glyphicon-chevron-down'
19402 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19409 var hours = this.time.getHours();
19410 var minutes = this.time.getMinutes();
19423 hours = hours - 12;
19427 hours = '0' + hours;
19431 minutes = '0' + minutes;
19434 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19435 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19436 this.pop.select('button', true).first().dom.innerHTML = period;
19442 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19444 var cls = ['bottom'];
19446 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19453 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19458 this.picker().addClass(cls.join('-'));
19462 Roo.each(cls, function(c){
19464 _this.picker().setTop(_this.inputEl().getHeight());
19468 _this.picker().setTop(0 - _this.picker().getHeight());
19473 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19477 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19484 onFocus : function()
19486 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19490 onBlur : function()
19492 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19498 this.picker().show();
19503 this.fireEvent('show', this, this.date);
19508 this.picker().hide();
19511 this.fireEvent('hide', this, this.date);
19514 setTime : function()
19517 this.setValue(this.time.format(this.format));
19519 this.fireEvent('select', this, this.date);
19524 onMousedown: function(e){
19525 e.stopPropagation();
19526 e.preventDefault();
19529 onIncrementHours: function()
19531 Roo.log('onIncrementHours');
19532 this.time = this.time.add(Date.HOUR, 1);
19537 onDecrementHours: function()
19539 Roo.log('onDecrementHours');
19540 this.time = this.time.add(Date.HOUR, -1);
19544 onIncrementMinutes: function()
19546 Roo.log('onIncrementMinutes');
19547 this.time = this.time.add(Date.MINUTE, 1);
19551 onDecrementMinutes: function()
19553 Roo.log('onDecrementMinutes');
19554 this.time = this.time.add(Date.MINUTE, -1);
19558 onTogglePeriod: function()
19560 Roo.log('onTogglePeriod');
19561 this.time = this.time.add(Date.HOUR, 12);
19568 Roo.apply(Roo.bootstrap.TimeField, {
19598 cls: 'btn btn-info ok',
19610 Roo.apply(Roo.bootstrap.TimeField, {
19614 cls: 'datepicker dropdown-menu',
19618 cls: 'datepicker-time',
19622 cls: 'table-condensed',
19624 Roo.bootstrap.TimeField.content,
19625 Roo.bootstrap.TimeField.footer
19644 * @class Roo.bootstrap.MonthField
19645 * @extends Roo.bootstrap.Input
19646 * Bootstrap MonthField class
19648 * @cfg {String} language default en
19651 * Create a new MonthField
19652 * @param {Object} config The config object
19655 Roo.bootstrap.MonthField = function(config){
19656 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19661 * Fires when this field show.
19662 * @param {Roo.bootstrap.MonthField} this
19663 * @param {Mixed} date The date value
19668 * Fires when this field hide.
19669 * @param {Roo.bootstrap.MonthField} this
19670 * @param {Mixed} date The date value
19675 * Fires when select a date.
19676 * @param {Roo.bootstrap.MonthField} this
19677 * @param {String} oldvalue The old value
19678 * @param {String} newvalue The new value
19684 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
19686 onRender: function(ct, position)
19689 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19691 this.language = this.language || 'en';
19692 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19693 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19695 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19696 this.isInline = false;
19697 this.isInput = true;
19698 this.component = this.el.select('.add-on', true).first() || false;
19699 this.component = (this.component && this.component.length === 0) ? false : this.component;
19700 this.hasInput = this.component && this.inputEL().length;
19702 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19704 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19706 this.picker().on('mousedown', this.onMousedown, this);
19707 this.picker().on('click', this.onClick, this);
19709 this.picker().addClass('datepicker-dropdown');
19711 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19712 v.setStyle('width', '189px');
19719 if(this.isInline) {
19725 setValue: function(v, suppressEvent)
19727 var o = this.getValue();
19729 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
19733 if(suppressEvent !== true){
19734 this.fireEvent('select', this, o, v);
19739 getValue: function()
19744 onClick: function(e)
19746 e.stopPropagation();
19747 e.preventDefault();
19749 var target = e.getTarget();
19751 if(target.nodeName.toLowerCase() === 'i'){
19752 target = Roo.get(target).dom.parentNode;
19755 var nodeName = target.nodeName;
19756 var className = target.className;
19757 var html = target.innerHTML;
19759 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
19763 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
19765 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19771 picker : function()
19773 return this.pickerEl;
19776 fillMonths: function()
19779 var months = this.picker().select('>.datepicker-months td', true).first();
19781 months.dom.innerHTML = '';
19787 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
19790 months.createChild(month);
19799 if(typeof(this.vIndex) == 'undefined' && this.value.length){
19800 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19803 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19804 e.removeClass('active');
19806 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19807 e.addClass('active');
19814 if(this.isInline) {
19818 this.picker().removeClass(['bottom', 'top']);
19820 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19822 * place to the top of element!
19826 this.picker().addClass('top');
19827 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19832 this.picker().addClass('bottom');
19834 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19837 onFocus : function()
19839 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19843 onBlur : function()
19845 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19847 var d = this.inputEl().getValue();
19856 this.picker().show();
19857 this.picker().select('>.datepicker-months', true).first().show();
19861 this.fireEvent('show', this, this.date);
19866 if(this.isInline) {
19869 this.picker().hide();
19870 this.fireEvent('hide', this, this.date);
19874 onMousedown: function(e)
19876 e.stopPropagation();
19877 e.preventDefault();
19882 Roo.bootstrap.MonthField.superclass.keyup.call(this);
19886 fireKey: function(e)
19888 if (!this.picker().isVisible()){
19889 if (e.keyCode == 27) {// allow escape to hide and re-show picker
19900 e.preventDefault();
19904 dir = e.keyCode == 37 ? -1 : 1;
19906 this.vIndex = this.vIndex + dir;
19908 if(this.vIndex < 0){
19912 if(this.vIndex > 11){
19916 if(isNaN(this.vIndex)){
19920 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19926 dir = e.keyCode == 38 ? -1 : 1;
19928 this.vIndex = this.vIndex + dir * 4;
19930 if(this.vIndex < 0){
19934 if(this.vIndex > 11){
19938 if(isNaN(this.vIndex)){
19942 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19947 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19948 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19952 e.preventDefault();
19955 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19956 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19972 this.picker().remove();
19977 Roo.apply(Roo.bootstrap.MonthField, {
19996 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19997 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20002 Roo.apply(Roo.bootstrap.MonthField, {
20006 cls: 'datepicker dropdown-menu roo-dynamic',
20010 cls: 'datepicker-months',
20014 cls: 'table-condensed',
20016 Roo.bootstrap.DateField.content
20036 * @class Roo.bootstrap.CheckBox
20037 * @extends Roo.bootstrap.Input
20038 * Bootstrap CheckBox class
20040 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20041 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20042 * @cfg {String} boxLabel The text that appears beside the checkbox
20043 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20044 * @cfg {Boolean} checked initnal the element
20045 * @cfg {Boolean} inline inline the element (default false)
20046 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20049 * Create a new CheckBox
20050 * @param {Object} config The config object
20053 Roo.bootstrap.CheckBox = function(config){
20054 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20059 * Fires when the element is checked or unchecked.
20060 * @param {Roo.bootstrap.CheckBox} this This input
20061 * @param {Boolean} checked The new checked value
20068 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
20070 inputType: 'checkbox',
20078 getAutoCreate : function()
20080 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20086 cfg.cls = 'form-group ' + this.inputType; //input-group
20089 cfg.cls += ' ' + this.inputType + '-inline';
20095 type : this.inputType,
20096 value : this.inputValue,
20097 cls : 'roo-' + this.inputType, //'form-box',
20098 placeholder : this.placeholder || ''
20102 if(this.inputType != 'radio'){
20106 cls : 'roo-hidden-value',
20107 value : this.checked ? this.valueOff : this.inputValue
20112 if (this.weight) { // Validity check?
20113 cfg.cls += " " + this.inputType + "-" + this.weight;
20116 if (this.disabled) {
20117 input.disabled=true;
20121 input.checked = this.checked;
20128 input.name = this.name;
20130 if(this.inputType != 'radio'){
20131 hidden.name = this.name;
20132 input.name = '_hidden_' + this.name;
20137 input.cls += ' input-' + this.size;
20142 ['xs','sm','md','lg'].map(function(size){
20143 if (settings[size]) {
20144 cfg.cls += ' col-' + size + '-' + settings[size];
20148 var inputblock = input;
20150 if (this.before || this.after) {
20153 cls : 'input-group',
20158 inputblock.cn.push({
20160 cls : 'input-group-addon',
20165 inputblock.cn.push(input);
20167 if(this.inputType != 'radio'){
20168 inputblock.cn.push(hidden);
20172 inputblock.cn.push({
20174 cls : 'input-group-addon',
20181 if (align ==='left' && this.fieldLabel.length) {
20182 // Roo.log("left and has label");
20187 cls : 'control-label',
20188 html : this.fieldLabel
20199 if(this.labelWidth > 12){
20200 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20203 if(this.labelWidth < 13 && this.labelmd == 0){
20204 this.labelmd = this.labelWidth;
20207 if(this.labellg > 0){
20208 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20209 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20212 if(this.labelmd > 0){
20213 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20214 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20217 if(this.labelsm > 0){
20218 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20219 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20222 if(this.labelxs > 0){
20223 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20224 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20227 } else if ( this.fieldLabel.length) {
20228 // Roo.log(" label");
20232 tag: this.boxLabel ? 'span' : 'label',
20234 cls: 'control-label box-input-label',
20235 //cls : 'input-group-addon',
20236 html : this.fieldLabel
20246 // Roo.log(" no label && no align");
20247 cfg.cn = [ inputblock ] ;
20253 var boxLabelCfg = {
20255 //'for': id, // box label is handled by onclick - so no for...
20257 html: this.boxLabel
20261 boxLabelCfg.tooltip = this.tooltip;
20264 cfg.cn.push(boxLabelCfg);
20267 if(this.inputType != 'radio'){
20268 cfg.cn.push(hidden);
20276 * return the real input element.
20278 inputEl: function ()
20280 return this.el.select('input.roo-' + this.inputType,true).first();
20282 hiddenEl: function ()
20284 return this.el.select('input.roo-hidden-value',true).first();
20287 labelEl: function()
20289 return this.el.select('label.control-label',true).first();
20291 /* depricated... */
20295 return this.labelEl();
20298 boxLabelEl: function()
20300 return this.el.select('label.box-label',true).first();
20303 initEvents : function()
20305 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20307 this.inputEl().on('click', this.onClick, this);
20309 if (this.boxLabel) {
20310 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
20313 this.startValue = this.getValue();
20316 Roo.bootstrap.CheckBox.register(this);
20320 onClick : function()
20322 this.setChecked(!this.checked);
20325 setChecked : function(state,suppressEvent)
20327 this.startValue = this.getValue();
20329 if(this.inputType == 'radio'){
20331 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20332 e.dom.checked = false;
20335 this.inputEl().dom.checked = true;
20337 this.inputEl().dom.value = this.inputValue;
20339 if(suppressEvent !== true){
20340 this.fireEvent('check', this, true);
20348 this.checked = state;
20350 this.inputEl().dom.checked = state;
20353 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20355 if(suppressEvent !== true){
20356 this.fireEvent('check', this, state);
20362 getValue : function()
20364 if(this.inputType == 'radio'){
20365 return this.getGroupValue();
20368 return this.hiddenEl().dom.value;
20372 getGroupValue : function()
20374 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20378 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20381 setValue : function(v,suppressEvent)
20383 if(this.inputType == 'radio'){
20384 this.setGroupValue(v, suppressEvent);
20388 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20393 setGroupValue : function(v, suppressEvent)
20395 this.startValue = this.getValue();
20397 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20398 e.dom.checked = false;
20400 if(e.dom.value == v){
20401 e.dom.checked = true;
20405 if(suppressEvent !== true){
20406 this.fireEvent('check', this, true);
20414 validate : function()
20418 (this.inputType == 'radio' && this.validateRadio()) ||
20419 (this.inputType == 'checkbox' && this.validateCheckbox())
20425 this.markInvalid();
20429 validateRadio : function()
20431 if(this.allowBlank){
20437 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20438 if(!e.dom.checked){
20450 validateCheckbox : function()
20453 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20454 //return (this.getValue() == this.inputValue) ? true : false;
20457 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20465 for(var i in group){
20470 r = (group[i].getValue() == group[i].inputValue) ? true : false;
20477 * Mark this field as valid
20479 markValid : function()
20483 this.fireEvent('valid', this);
20485 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20488 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20495 if(this.inputType == 'radio'){
20496 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20497 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20498 e.findParent('.form-group', false, true).addClass(_this.validClass);
20505 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20506 this.el.findParent('.form-group', false, true).addClass(this.validClass);
20510 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20516 for(var i in group){
20517 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20518 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20523 * Mark this field as invalid
20524 * @param {String} msg The validation message
20526 markInvalid : function(msg)
20528 if(this.allowBlank){
20534 this.fireEvent('invalid', this, msg);
20536 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20539 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20543 label.markInvalid();
20546 if(this.inputType == 'radio'){
20547 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20548 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20549 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20556 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20557 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20561 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20567 for(var i in group){
20568 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20569 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20574 clearInvalid : function()
20576 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20578 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20580 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20583 label.iconEl.removeClass(label.validClass);
20584 label.iconEl.removeClass(label.invalidClass);
20588 disable : function()
20590 if(this.inputType != 'radio'){
20591 Roo.bootstrap.CheckBox.superclass.disable.call(this);
20598 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20599 _this.getActionEl().addClass(this.disabledClass);
20600 e.dom.disabled = true;
20604 this.disabled = true;
20605 this.fireEvent("disable", this);
20609 enable : function()
20611 if(this.inputType != 'radio'){
20612 Roo.bootstrap.CheckBox.superclass.enable.call(this);
20619 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20620 _this.getActionEl().removeClass(this.disabledClass);
20621 e.dom.disabled = false;
20625 this.disabled = false;
20626 this.fireEvent("enable", this);
20632 Roo.apply(Roo.bootstrap.CheckBox, {
20637 * register a CheckBox Group
20638 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20640 register : function(checkbox)
20642 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20643 this.groups[checkbox.groupId] = {};
20646 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20650 this.groups[checkbox.groupId][checkbox.name] = checkbox;
20654 * fetch a CheckBox Group based on the group ID
20655 * @param {string} the group ID
20656 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20658 get: function(groupId) {
20659 if (typeof(this.groups[groupId]) == 'undefined') {
20663 return this.groups[groupId] ;
20676 * @class Roo.bootstrap.Radio
20677 * @extends Roo.bootstrap.Component
20678 * Bootstrap Radio class
20679 * @cfg {String} boxLabel - the label associated
20680 * @cfg {String} value - the value of radio
20683 * Create a new Radio
20684 * @param {Object} config The config object
20686 Roo.bootstrap.Radio = function(config){
20687 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
20691 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
20697 getAutoCreate : function()
20701 cls : 'form-group radio',
20706 html : this.boxLabel
20714 initEvents : function()
20716 this.parent().register(this);
20718 this.el.on('click', this.onClick, this);
20722 onClick : function()
20724 this.setChecked(true);
20727 setChecked : function(state, suppressEvent)
20729 this.parent().setValue(this.value, suppressEvent);
20744 * @class Roo.bootstrap.SecurePass
20745 * @extends Roo.bootstrap.Input
20746 * Bootstrap SecurePass class
20750 * Create a new SecurePass
20751 * @param {Object} config The config object
20754 Roo.bootstrap.SecurePass = function (config) {
20755 // these go here, so the translation tool can replace them..
20757 PwdEmpty: "Please type a password, and then retype it to confirm.",
20758 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20759 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20760 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20761 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20762 FNInPwd: "Your password can't contain your first name. Please type a different password.",
20763 LNInPwd: "Your password can't contain your last name. Please type a different password.",
20764 TooWeak: "Your password is Too Weak."
20766 this.meterLabel = "Password strength:";
20767 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
20768 this.meterClass = [
20769 "roo-password-meter-tooweak",
20770 "roo-password-meter-weak",
20771 "roo-password-meter-medium",
20772 "roo-password-meter-strong",
20773 "roo-password-meter-grey"
20778 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
20781 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
20783 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
20785 * PwdEmpty: "Please type a password, and then retype it to confirm.",
20786 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20787 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20788 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20789 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20790 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
20791 * LNInPwd: "Your password can't contain your last name. Please type a different password."
20801 * @cfg {String/Object} Label for the strength meter (defaults to
20802 * 'Password strength:')
20807 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
20808 * ['Weak', 'Medium', 'Strong'])
20811 pwdStrengths: false,
20824 initEvents: function ()
20826 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
20828 if (this.el.is('input[type=password]') && Roo.isSafari) {
20829 this.el.on('keydown', this.SafariOnKeyDown, this);
20832 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
20835 onRender: function (ct, position)
20837 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
20838 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
20839 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
20841 this.trigger.createChild({
20846 cls: 'roo-password-meter-grey col-xs-12',
20849 //width: this.meterWidth + 'px'
20853 cls: 'roo-password-meter-text'
20859 if (this.hideTrigger) {
20860 this.trigger.setDisplayed(false);
20862 this.setSize(this.width || '', this.height || '');
20865 onDestroy: function ()
20867 if (this.trigger) {
20868 this.trigger.removeAllListeners();
20869 this.trigger.remove();
20872 this.wrap.remove();
20874 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
20877 checkStrength: function ()
20879 var pwd = this.inputEl().getValue();
20880 if (pwd == this._lastPwd) {
20885 if (this.ClientSideStrongPassword(pwd)) {
20887 } else if (this.ClientSideMediumPassword(pwd)) {
20889 } else if (this.ClientSideWeakPassword(pwd)) {
20895 Roo.log('strength1: ' + strength);
20897 //var pm = this.trigger.child('div/div/div').dom;
20898 var pm = this.trigger.child('div/div');
20899 pm.removeClass(this.meterClass);
20900 pm.addClass(this.meterClass[strength]);
20903 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
20905 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
20907 this._lastPwd = pwd;
20911 Roo.bootstrap.SecurePass.superclass.reset.call(this);
20913 this._lastPwd = '';
20915 var pm = this.trigger.child('div/div');
20916 pm.removeClass(this.meterClass);
20917 pm.addClass('roo-password-meter-grey');
20920 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
20923 this.inputEl().dom.type='password';
20926 validateValue: function (value)
20929 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
20932 if (value.length == 0) {
20933 if (this.allowBlank) {
20934 this.clearInvalid();
20938 this.markInvalid(this.errors.PwdEmpty);
20939 this.errorMsg = this.errors.PwdEmpty;
20947 if ('[\x21-\x7e]*'.match(value)) {
20948 this.markInvalid(this.errors.PwdBadChar);
20949 this.errorMsg = this.errors.PwdBadChar;
20952 if (value.length < 6) {
20953 this.markInvalid(this.errors.PwdShort);
20954 this.errorMsg = this.errors.PwdShort;
20957 if (value.length > 16) {
20958 this.markInvalid(this.errors.PwdLong);
20959 this.errorMsg = this.errors.PwdLong;
20963 if (this.ClientSideStrongPassword(value)) {
20965 } else if (this.ClientSideMediumPassword(value)) {
20967 } else if (this.ClientSideWeakPassword(value)) {
20974 if (strength < 2) {
20975 //this.markInvalid(this.errors.TooWeak);
20976 this.errorMsg = this.errors.TooWeak;
20981 console.log('strength2: ' + strength);
20983 //var pm = this.trigger.child('div/div/div').dom;
20985 var pm = this.trigger.child('div/div');
20986 pm.removeClass(this.meterClass);
20987 pm.addClass(this.meterClass[strength]);
20989 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
20991 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
20993 this.errorMsg = '';
20997 CharacterSetChecks: function (type)
21000 this.fResult = false;
21003 isctype: function (character, type)
21006 case this.kCapitalLetter:
21007 if (character >= 'A' && character <= 'Z') {
21012 case this.kSmallLetter:
21013 if (character >= 'a' && character <= 'z') {
21019 if (character >= '0' && character <= '9') {
21024 case this.kPunctuation:
21025 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21036 IsLongEnough: function (pwd, size)
21038 return !(pwd == null || isNaN(size) || pwd.length < size);
21041 SpansEnoughCharacterSets: function (word, nb)
21043 if (!this.IsLongEnough(word, nb))
21048 var characterSetChecks = new Array(
21049 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21050 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21053 for (var index = 0; index < word.length; ++index) {
21054 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21055 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21056 characterSetChecks[nCharSet].fResult = true;
21063 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21064 if (characterSetChecks[nCharSet].fResult) {
21069 if (nCharSets < nb) {
21075 ClientSideStrongPassword: function (pwd)
21077 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21080 ClientSideMediumPassword: function (pwd)
21082 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21085 ClientSideWeakPassword: function (pwd)
21087 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21090 })//<script type="text/javascript">
21093 * Based Ext JS Library 1.1.1
21094 * Copyright(c) 2006-2007, Ext JS, LLC.
21100 * @class Roo.HtmlEditorCore
21101 * @extends Roo.Component
21102 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21104 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21107 Roo.HtmlEditorCore = function(config){
21110 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21115 * @event initialize
21116 * Fires when the editor is fully initialized (including the iframe)
21117 * @param {Roo.HtmlEditorCore} this
21122 * Fires when the editor is first receives the focus. Any insertion must wait
21123 * until after this event.
21124 * @param {Roo.HtmlEditorCore} this
21128 * @event beforesync
21129 * Fires before the textarea is updated with content from the editor iframe. Return false
21130 * to cancel the sync.
21131 * @param {Roo.HtmlEditorCore} this
21132 * @param {String} html
21136 * @event beforepush
21137 * Fires before the iframe editor is updated with content from the textarea. Return false
21138 * to cancel the push.
21139 * @param {Roo.HtmlEditorCore} this
21140 * @param {String} html
21145 * Fires when the textarea is updated with content from the editor iframe.
21146 * @param {Roo.HtmlEditorCore} this
21147 * @param {String} html
21152 * Fires when the iframe editor is updated with content from the textarea.
21153 * @param {Roo.HtmlEditorCore} this
21154 * @param {String} html
21159 * @event editorevent
21160 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21161 * @param {Roo.HtmlEditorCore} this
21167 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21169 // defaults : white / black...
21170 this.applyBlacklists();
21177 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
21181 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
21187 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21192 * @cfg {Number} height (in pixels)
21196 * @cfg {Number} width (in pixels)
21201 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21204 stylesheets: false,
21209 // private properties
21210 validationEvent : false,
21212 initialized : false,
21214 sourceEditMode : false,
21215 onFocus : Roo.emptyFn,
21217 hideMode:'offsets',
21221 // blacklist + whitelisted elements..
21228 * Protected method that will not generally be called directly. It
21229 * is called when the editor initializes the iframe with HTML contents. Override this method if you
21230 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21232 getDocMarkup : function(){
21236 // inherit styels from page...??
21237 if (this.stylesheets === false) {
21239 Roo.get(document.head).select('style').each(function(node) {
21240 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21243 Roo.get(document.head).select('link').each(function(node) {
21244 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21247 } else if (!this.stylesheets.length) {
21249 st = '<style type="text/css">' +
21250 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21256 st += '<style type="text/css">' +
21257 'IMG { cursor: pointer } ' +
21261 return '<html><head>' + st +
21262 //<style type="text/css">' +
21263 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21265 ' </head><body class="roo-htmleditor-body"></body></html>';
21269 onRender : function(ct, position)
21272 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21273 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21276 this.el.dom.style.border = '0 none';
21277 this.el.dom.setAttribute('tabIndex', -1);
21278 this.el.addClass('x-hidden hide');
21282 if(Roo.isIE){ // fix IE 1px bogus margin
21283 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21287 this.frameId = Roo.id();
21291 var iframe = this.owner.wrap.createChild({
21293 cls: 'form-control', // bootstrap..
21295 name: this.frameId,
21296 frameBorder : 'no',
21297 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
21302 this.iframe = iframe.dom;
21304 this.assignDocWin();
21306 this.doc.designMode = 'on';
21309 this.doc.write(this.getDocMarkup());
21313 var task = { // must defer to wait for browser to be ready
21315 //console.log("run task?" + this.doc.readyState);
21316 this.assignDocWin();
21317 if(this.doc.body || this.doc.readyState == 'complete'){
21319 this.doc.designMode="on";
21323 Roo.TaskMgr.stop(task);
21324 this.initEditor.defer(10, this);
21331 Roo.TaskMgr.start(task);
21336 onResize : function(w, h)
21338 Roo.log('resize: ' +w + ',' + h );
21339 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21343 if(typeof w == 'number'){
21345 this.iframe.style.width = w + 'px';
21347 if(typeof h == 'number'){
21349 this.iframe.style.height = h + 'px';
21351 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21358 * Toggles the editor between standard and source edit mode.
21359 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21361 toggleSourceEdit : function(sourceEditMode){
21363 this.sourceEditMode = sourceEditMode === true;
21365 if(this.sourceEditMode){
21367 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
21370 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21371 //this.iframe.className = '';
21374 //this.setSize(this.owner.wrap.getSize());
21375 //this.fireEvent('editmodechange', this, this.sourceEditMode);
21382 * Protected method that will not generally be called directly. If you need/want
21383 * custom HTML cleanup, this is the method you should override.
21384 * @param {String} html The HTML to be cleaned
21385 * return {String} The cleaned HTML
21387 cleanHtml : function(html){
21388 html = String(html);
21389 if(html.length > 5){
21390 if(Roo.isSafari){ // strip safari nonsense
21391 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21394 if(html == ' '){
21401 * HTML Editor -> Textarea
21402 * Protected method that will not generally be called directly. Syncs the contents
21403 * of the editor iframe with the textarea.
21405 syncValue : function(){
21406 if(this.initialized){
21407 var bd = (this.doc.body || this.doc.documentElement);
21408 //this.cleanUpPaste(); -- this is done else where and causes havoc..
21409 var html = bd.innerHTML;
21411 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21412 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21414 html = '<div style="'+m[0]+'">' + html + '</div>';
21417 html = this.cleanHtml(html);
21418 // fix up the special chars.. normaly like back quotes in word...
21419 // however we do not want to do this with chinese..
21420 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21421 var cc = b.charCodeAt();
21423 (cc >= 0x4E00 && cc < 0xA000 ) ||
21424 (cc >= 0x3400 && cc < 0x4E00 ) ||
21425 (cc >= 0xf900 && cc < 0xfb00 )
21431 if(this.owner.fireEvent('beforesync', this, html) !== false){
21432 this.el.dom.value = html;
21433 this.owner.fireEvent('sync', this, html);
21439 * Protected method that will not generally be called directly. Pushes the value of the textarea
21440 * into the iframe editor.
21442 pushValue : function(){
21443 if(this.initialized){
21444 var v = this.el.dom.value.trim();
21446 // if(v.length < 1){
21450 if(this.owner.fireEvent('beforepush', this, v) !== false){
21451 var d = (this.doc.body || this.doc.documentElement);
21453 this.cleanUpPaste();
21454 this.el.dom.value = d.innerHTML;
21455 this.owner.fireEvent('push', this, v);
21461 deferFocus : function(){
21462 this.focus.defer(10, this);
21466 focus : function(){
21467 if(this.win && !this.sourceEditMode){
21474 assignDocWin: function()
21476 var iframe = this.iframe;
21479 this.doc = iframe.contentWindow.document;
21480 this.win = iframe.contentWindow;
21482 // if (!Roo.get(this.frameId)) {
21485 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21486 // this.win = Roo.get(this.frameId).dom.contentWindow;
21488 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21492 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21493 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21498 initEditor : function(){
21499 //console.log("INIT EDITOR");
21500 this.assignDocWin();
21504 this.doc.designMode="on";
21506 this.doc.write(this.getDocMarkup());
21509 var dbody = (this.doc.body || this.doc.documentElement);
21510 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21511 // this copies styles from the containing element into thsi one..
21512 // not sure why we need all of this..
21513 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21515 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21516 //ss['background-attachment'] = 'fixed'; // w3c
21517 dbody.bgProperties = 'fixed'; // ie
21518 //Roo.DomHelper.applyStyles(dbody, ss);
21519 Roo.EventManager.on(this.doc, {
21520 //'mousedown': this.onEditorEvent,
21521 'mouseup': this.onEditorEvent,
21522 'dblclick': this.onEditorEvent,
21523 'click': this.onEditorEvent,
21524 'keyup': this.onEditorEvent,
21529 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21531 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21532 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21534 this.initialized = true;
21536 this.owner.fireEvent('initialize', this);
21541 onDestroy : function(){
21547 //for (var i =0; i < this.toolbars.length;i++) {
21548 // // fixme - ask toolbars for heights?
21549 // this.toolbars[i].onDestroy();
21552 //this.wrap.dom.innerHTML = '';
21553 //this.wrap.remove();
21558 onFirstFocus : function(){
21560 this.assignDocWin();
21563 this.activated = true;
21566 if(Roo.isGecko){ // prevent silly gecko errors
21568 var s = this.win.getSelection();
21569 if(!s.focusNode || s.focusNode.nodeType != 3){
21570 var r = s.getRangeAt(0);
21571 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21576 this.execCmd('useCSS', true);
21577 this.execCmd('styleWithCSS', false);
21580 this.owner.fireEvent('activate', this);
21584 adjustFont: function(btn){
21585 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21586 //if(Roo.isSafari){ // safari
21589 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21590 if(Roo.isSafari){ // safari
21591 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21592 v = (v < 10) ? 10 : v;
21593 v = (v > 48) ? 48 : v;
21594 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21599 v = Math.max(1, v+adjust);
21601 this.execCmd('FontSize', v );
21604 onEditorEvent : function(e)
21606 this.owner.fireEvent('editorevent', this, e);
21607 // this.updateToolbar();
21608 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21611 insertTag : function(tg)
21613 // could be a bit smarter... -> wrap the current selected tRoo..
21614 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
21616 range = this.createRange(this.getSelection());
21617 var wrappingNode = this.doc.createElement(tg.toLowerCase());
21618 wrappingNode.appendChild(range.extractContents());
21619 range.insertNode(wrappingNode);
21626 this.execCmd("formatblock", tg);
21630 insertText : function(txt)
21634 var range = this.createRange();
21635 range.deleteContents();
21636 //alert(Sender.getAttribute('label'));
21638 range.insertNode(this.doc.createTextNode(txt));
21644 * Executes a Midas editor command on the editor document and performs necessary focus and
21645 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21646 * @param {String} cmd The Midas command
21647 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21649 relayCmd : function(cmd, value){
21651 this.execCmd(cmd, value);
21652 this.owner.fireEvent('editorevent', this);
21653 //this.updateToolbar();
21654 this.owner.deferFocus();
21658 * Executes a Midas editor command directly on the editor document.
21659 * For visual commands, you should use {@link #relayCmd} instead.
21660 * <b>This should only be called after the editor is initialized.</b>
21661 * @param {String} cmd The Midas command
21662 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21664 execCmd : function(cmd, value){
21665 this.doc.execCommand(cmd, false, value === undefined ? null : value);
21672 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21674 * @param {String} text | dom node..
21676 insertAtCursor : function(text)
21679 if(!this.activated){
21685 var r = this.doc.selection.createRange();
21696 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21700 // from jquery ui (MIT licenced)
21702 var win = this.win;
21704 if (win.getSelection && win.getSelection().getRangeAt) {
21705 range = win.getSelection().getRangeAt(0);
21706 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21707 range.insertNode(node);
21708 } else if (win.document.selection && win.document.selection.createRange) {
21709 // no firefox support
21710 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21711 win.document.selection.createRange().pasteHTML(txt);
21713 // no firefox support
21714 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21715 this.execCmd('InsertHTML', txt);
21724 mozKeyPress : function(e){
21726 var c = e.getCharCode(), cmd;
21729 c = String.fromCharCode(c).toLowerCase();
21743 this.cleanUpPaste.defer(100, this);
21751 e.preventDefault();
21759 fixKeys : function(){ // load time branching for fastest keydown performance
21761 return function(e){
21762 var k = e.getKey(), r;
21765 r = this.doc.selection.createRange();
21768 r.pasteHTML('    ');
21775 r = this.doc.selection.createRange();
21777 var target = r.parentElement();
21778 if(!target || target.tagName.toLowerCase() != 'li'){
21780 r.pasteHTML('<br />');
21786 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21787 this.cleanUpPaste.defer(100, this);
21793 }else if(Roo.isOpera){
21794 return function(e){
21795 var k = e.getKey();
21799 this.execCmd('InsertHTML','    ');
21802 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21803 this.cleanUpPaste.defer(100, this);
21808 }else if(Roo.isSafari){
21809 return function(e){
21810 var k = e.getKey();
21814 this.execCmd('InsertText','\t');
21818 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21819 this.cleanUpPaste.defer(100, this);
21827 getAllAncestors: function()
21829 var p = this.getSelectedNode();
21832 a.push(p); // push blank onto stack..
21833 p = this.getParentElement();
21837 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21841 a.push(this.doc.body);
21845 lastSelNode : false,
21848 getSelection : function()
21850 this.assignDocWin();
21851 return Roo.isIE ? this.doc.selection : this.win.getSelection();
21854 getSelectedNode: function()
21856 // this may only work on Gecko!!!
21858 // should we cache this!!!!
21863 var range = this.createRange(this.getSelection()).cloneRange();
21866 var parent = range.parentElement();
21868 var testRange = range.duplicate();
21869 testRange.moveToElementText(parent);
21870 if (testRange.inRange(range)) {
21873 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21876 parent = parent.parentElement;
21881 // is ancestor a text element.
21882 var ac = range.commonAncestorContainer;
21883 if (ac.nodeType == 3) {
21884 ac = ac.parentNode;
21887 var ar = ac.childNodes;
21890 var other_nodes = [];
21891 var has_other_nodes = false;
21892 for (var i=0;i<ar.length;i++) {
21893 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
21896 // fullly contained node.
21898 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21903 // probably selected..
21904 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21905 other_nodes.push(ar[i]);
21909 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
21914 has_other_nodes = true;
21916 if (!nodes.length && other_nodes.length) {
21917 nodes= other_nodes;
21919 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21925 createRange: function(sel)
21927 // this has strange effects when using with
21928 // top toolbar - not sure if it's a great idea.
21929 //this.editor.contentWindow.focus();
21930 if (typeof sel != "undefined") {
21932 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21934 return this.doc.createRange();
21937 return this.doc.createRange();
21940 getParentElement: function()
21943 this.assignDocWin();
21944 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21946 var range = this.createRange(sel);
21949 var p = range.commonAncestorContainer;
21950 while (p.nodeType == 3) { // text node
21961 * Range intersection.. the hard stuff...
21965 * [ -- selected range --- ]
21969 * if end is before start or hits it. fail.
21970 * if start is after end or hits it fail.
21972 * if either hits (but other is outside. - then it's not
21978 // @see http://www.thismuchiknow.co.uk/?p=64.
21979 rangeIntersectsNode : function(range, node)
21981 var nodeRange = node.ownerDocument.createRange();
21983 nodeRange.selectNode(node);
21985 nodeRange.selectNodeContents(node);
21988 var rangeStartRange = range.cloneRange();
21989 rangeStartRange.collapse(true);
21991 var rangeEndRange = range.cloneRange();
21992 rangeEndRange.collapse(false);
21994 var nodeStartRange = nodeRange.cloneRange();
21995 nodeStartRange.collapse(true);
21997 var nodeEndRange = nodeRange.cloneRange();
21998 nodeEndRange.collapse(false);
22000 return rangeStartRange.compareBoundaryPoints(
22001 Range.START_TO_START, nodeEndRange) == -1 &&
22002 rangeEndRange.compareBoundaryPoints(
22003 Range.START_TO_START, nodeStartRange) == 1;
22007 rangeCompareNode : function(range, node)
22009 var nodeRange = node.ownerDocument.createRange();
22011 nodeRange.selectNode(node);
22013 nodeRange.selectNodeContents(node);
22017 range.collapse(true);
22019 nodeRange.collapse(true);
22021 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22022 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
22024 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22026 var nodeIsBefore = ss == 1;
22027 var nodeIsAfter = ee == -1;
22029 if (nodeIsBefore && nodeIsAfter) {
22032 if (!nodeIsBefore && nodeIsAfter) {
22033 return 1; //right trailed.
22036 if (nodeIsBefore && !nodeIsAfter) {
22037 return 2; // left trailed.
22043 // private? - in a new class?
22044 cleanUpPaste : function()
22046 // cleans up the whole document..
22047 Roo.log('cleanuppaste');
22049 this.cleanUpChildren(this.doc.body);
22050 var clean = this.cleanWordChars(this.doc.body.innerHTML);
22051 if (clean != this.doc.body.innerHTML) {
22052 this.doc.body.innerHTML = clean;
22057 cleanWordChars : function(input) {// change the chars to hex code
22058 var he = Roo.HtmlEditorCore;
22060 var output = input;
22061 Roo.each(he.swapCodes, function(sw) {
22062 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22064 output = output.replace(swapper, sw[1]);
22071 cleanUpChildren : function (n)
22073 if (!n.childNodes.length) {
22076 for (var i = n.childNodes.length-1; i > -1 ; i--) {
22077 this.cleanUpChild(n.childNodes[i]);
22084 cleanUpChild : function (node)
22087 //console.log(node);
22088 if (node.nodeName == "#text") {
22089 // clean up silly Windows -- stuff?
22092 if (node.nodeName == "#comment") {
22093 node.parentNode.removeChild(node);
22094 // clean up silly Windows -- stuff?
22097 var lcname = node.tagName.toLowerCase();
22098 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22099 // whitelist of tags..
22101 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22103 node.parentNode.removeChild(node);
22108 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22110 // remove <a name=....> as rendering on yahoo mailer is borked with this.
22111 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22113 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22114 // remove_keep_children = true;
22117 if (remove_keep_children) {
22118 this.cleanUpChildren(node);
22119 // inserts everything just before this node...
22120 while (node.childNodes.length) {
22121 var cn = node.childNodes[0];
22122 node.removeChild(cn);
22123 node.parentNode.insertBefore(cn, node);
22125 node.parentNode.removeChild(node);
22129 if (!node.attributes || !node.attributes.length) {
22130 this.cleanUpChildren(node);
22134 function cleanAttr(n,v)
22137 if (v.match(/^\./) || v.match(/^\//)) {
22140 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
22143 if (v.match(/^#/)) {
22146 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22147 node.removeAttribute(n);
22151 var cwhite = this.cwhite;
22152 var cblack = this.cblack;
22154 function cleanStyle(n,v)
22156 if (v.match(/expression/)) { //XSS?? should we even bother..
22157 node.removeAttribute(n);
22161 var parts = v.split(/;/);
22164 Roo.each(parts, function(p) {
22165 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22169 var l = p.split(':').shift().replace(/\s+/g,'');
22170 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22172 if ( cwhite.length && cblack.indexOf(l) > -1) {
22173 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22174 //node.removeAttribute(n);
22178 // only allow 'c whitelisted system attributes'
22179 if ( cwhite.length && cwhite.indexOf(l) < 0) {
22180 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22181 //node.removeAttribute(n);
22191 if (clean.length) {
22192 node.setAttribute(n, clean.join(';'));
22194 node.removeAttribute(n);
22200 for (var i = node.attributes.length-1; i > -1 ; i--) {
22201 var a = node.attributes[i];
22204 if (a.name.toLowerCase().substr(0,2)=='on') {
22205 node.removeAttribute(a.name);
22208 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22209 node.removeAttribute(a.name);
22212 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22213 cleanAttr(a.name,a.value); // fixme..
22216 if (a.name == 'style') {
22217 cleanStyle(a.name,a.value);
22220 /// clean up MS crap..
22221 // tecnically this should be a list of valid class'es..
22224 if (a.name == 'class') {
22225 if (a.value.match(/^Mso/)) {
22226 node.className = '';
22229 if (a.value.match(/^body$/)) {
22230 node.className = '';
22241 this.cleanUpChildren(node);
22247 * Clean up MS wordisms...
22249 cleanWord : function(node)
22254 this.cleanWord(this.doc.body);
22257 if (node.nodeName == "#text") {
22258 // clean up silly Windows -- stuff?
22261 if (node.nodeName == "#comment") {
22262 node.parentNode.removeChild(node);
22263 // clean up silly Windows -- stuff?
22267 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22268 node.parentNode.removeChild(node);
22272 // remove - but keep children..
22273 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22274 while (node.childNodes.length) {
22275 var cn = node.childNodes[0];
22276 node.removeChild(cn);
22277 node.parentNode.insertBefore(cn, node);
22279 node.parentNode.removeChild(node);
22280 this.iterateChildren(node, this.cleanWord);
22284 if (node.className.length) {
22286 var cn = node.className.split(/\W+/);
22288 Roo.each(cn, function(cls) {
22289 if (cls.match(/Mso[a-zA-Z]+/)) {
22294 node.className = cna.length ? cna.join(' ') : '';
22296 node.removeAttribute("class");
22300 if (node.hasAttribute("lang")) {
22301 node.removeAttribute("lang");
22304 if (node.hasAttribute("style")) {
22306 var styles = node.getAttribute("style").split(";");
22308 Roo.each(styles, function(s) {
22309 if (!s.match(/:/)) {
22312 var kv = s.split(":");
22313 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22316 // what ever is left... we allow.
22319 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22320 if (!nstyle.length) {
22321 node.removeAttribute('style');
22324 this.iterateChildren(node, this.cleanWord);
22330 * iterateChildren of a Node, calling fn each time, using this as the scole..
22331 * @param {DomNode} node node to iterate children of.
22332 * @param {Function} fn method of this class to call on each item.
22334 iterateChildren : function(node, fn)
22336 if (!node.childNodes.length) {
22339 for (var i = node.childNodes.length-1; i > -1 ; i--) {
22340 fn.call(this, node.childNodes[i])
22346 * cleanTableWidths.
22348 * Quite often pasting from word etc.. results in tables with column and widths.
22349 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22352 cleanTableWidths : function(node)
22357 this.cleanTableWidths(this.doc.body);
22362 if (node.nodeName == "#text" || node.nodeName == "#comment") {
22365 Roo.log(node.tagName);
22366 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22367 this.iterateChildren(node, this.cleanTableWidths);
22370 if (node.hasAttribute('width')) {
22371 node.removeAttribute('width');
22375 if (node.hasAttribute("style")) {
22378 var styles = node.getAttribute("style").split(";");
22380 Roo.each(styles, function(s) {
22381 if (!s.match(/:/)) {
22384 var kv = s.split(":");
22385 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22388 // what ever is left... we allow.
22391 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22392 if (!nstyle.length) {
22393 node.removeAttribute('style');
22397 this.iterateChildren(node, this.cleanTableWidths);
22405 domToHTML : function(currentElement, depth, nopadtext) {
22407 depth = depth || 0;
22408 nopadtext = nopadtext || false;
22410 if (!currentElement) {
22411 return this.domToHTML(this.doc.body);
22414 //Roo.log(currentElement);
22416 var allText = false;
22417 var nodeName = currentElement.nodeName;
22418 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22420 if (nodeName == '#text') {
22422 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22427 if (nodeName != 'BODY') {
22430 // Prints the node tagName, such as <A>, <IMG>, etc
22433 for(i = 0; i < currentElement.attributes.length;i++) {
22435 var aname = currentElement.attributes.item(i).name;
22436 if (!currentElement.attributes.item(i).value.length) {
22439 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22442 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22451 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22454 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22459 // Traverse the tree
22461 var currentElementChild = currentElement.childNodes.item(i);
22462 var allText = true;
22463 var innerHTML = '';
22465 while (currentElementChild) {
22466 // Formatting code (indent the tree so it looks nice on the screen)
22467 var nopad = nopadtext;
22468 if (lastnode == 'SPAN') {
22472 if (currentElementChild.nodeName == '#text') {
22473 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22474 toadd = nopadtext ? toadd : toadd.trim();
22475 if (!nopad && toadd.length > 80) {
22476 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
22478 innerHTML += toadd;
22481 currentElementChild = currentElement.childNodes.item(i);
22487 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
22489 // Recursively traverse the tree structure of the child node
22490 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
22491 lastnode = currentElementChild.nodeName;
22493 currentElementChild=currentElement.childNodes.item(i);
22499 // The remaining code is mostly for formatting the tree
22500 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
22505 ret+= "</"+tagName+">";
22511 applyBlacklists : function()
22513 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
22514 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
22518 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22519 if (b.indexOf(tag) > -1) {
22522 this.white.push(tag);
22526 Roo.each(w, function(tag) {
22527 if (b.indexOf(tag) > -1) {
22530 if (this.white.indexOf(tag) > -1) {
22533 this.white.push(tag);
22538 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22539 if (w.indexOf(tag) > -1) {
22542 this.black.push(tag);
22546 Roo.each(b, function(tag) {
22547 if (w.indexOf(tag) > -1) {
22550 if (this.black.indexOf(tag) > -1) {
22553 this.black.push(tag);
22558 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
22559 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
22563 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22564 if (b.indexOf(tag) > -1) {
22567 this.cwhite.push(tag);
22571 Roo.each(w, function(tag) {
22572 if (b.indexOf(tag) > -1) {
22575 if (this.cwhite.indexOf(tag) > -1) {
22578 this.cwhite.push(tag);
22583 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22584 if (w.indexOf(tag) > -1) {
22587 this.cblack.push(tag);
22591 Roo.each(b, function(tag) {
22592 if (w.indexOf(tag) > -1) {
22595 if (this.cblack.indexOf(tag) > -1) {
22598 this.cblack.push(tag);
22603 setStylesheets : function(stylesheets)
22605 if(typeof(stylesheets) == 'string'){
22606 Roo.get(this.iframe.contentDocument.head).createChild({
22608 rel : 'stylesheet',
22617 Roo.each(stylesheets, function(s) {
22622 Roo.get(_this.iframe.contentDocument.head).createChild({
22624 rel : 'stylesheet',
22633 removeStylesheets : function()
22637 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22642 // hide stuff that is not compatible
22656 * @event specialkey
22660 * @cfg {String} fieldClass @hide
22663 * @cfg {String} focusClass @hide
22666 * @cfg {String} autoCreate @hide
22669 * @cfg {String} inputType @hide
22672 * @cfg {String} invalidClass @hide
22675 * @cfg {String} invalidText @hide
22678 * @cfg {String} msgFx @hide
22681 * @cfg {String} validateOnBlur @hide
22685 Roo.HtmlEditorCore.white = [
22686 'area', 'br', 'img', 'input', 'hr', 'wbr',
22688 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
22689 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
22690 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
22691 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
22692 'table', 'ul', 'xmp',
22694 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
22697 'dir', 'menu', 'ol', 'ul', 'dl',
22703 Roo.HtmlEditorCore.black = [
22704 // 'embed', 'object', // enable - backend responsiblity to clean thiese
22706 'base', 'basefont', 'bgsound', 'blink', 'body',
22707 'frame', 'frameset', 'head', 'html', 'ilayer',
22708 'iframe', 'layer', 'link', 'meta', 'object',
22709 'script', 'style' ,'title', 'xml' // clean later..
22711 Roo.HtmlEditorCore.clean = [
22712 'script', 'style', 'title', 'xml'
22714 Roo.HtmlEditorCore.remove = [
22719 Roo.HtmlEditorCore.ablack = [
22723 Roo.HtmlEditorCore.aclean = [
22724 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
22728 Roo.HtmlEditorCore.pwhite= [
22729 'http', 'https', 'mailto'
22732 // white listed style attributes.
22733 Roo.HtmlEditorCore.cwhite= [
22734 // 'text-align', /// default is to allow most things..
22740 // black listed style attributes.
22741 Roo.HtmlEditorCore.cblack= [
22742 // 'font-size' -- this can be set by the project
22746 Roo.HtmlEditorCore.swapCodes =[
22765 * @class Roo.bootstrap.HtmlEditor
22766 * @extends Roo.bootstrap.TextArea
22767 * Bootstrap HtmlEditor class
22770 * Create a new HtmlEditor
22771 * @param {Object} config The config object
22774 Roo.bootstrap.HtmlEditor = function(config){
22775 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
22776 if (!this.toolbars) {
22777 this.toolbars = [];
22780 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22783 * @event initialize
22784 * Fires when the editor is fully initialized (including the iframe)
22785 * @param {HtmlEditor} this
22790 * Fires when the editor is first receives the focus. Any insertion must wait
22791 * until after this event.
22792 * @param {HtmlEditor} this
22796 * @event beforesync
22797 * Fires before the textarea is updated with content from the editor iframe. Return false
22798 * to cancel the sync.
22799 * @param {HtmlEditor} this
22800 * @param {String} html
22804 * @event beforepush
22805 * Fires before the iframe editor is updated with content from the textarea. Return false
22806 * to cancel the push.
22807 * @param {HtmlEditor} this
22808 * @param {String} html
22813 * Fires when the textarea is updated with content from the editor iframe.
22814 * @param {HtmlEditor} this
22815 * @param {String} html
22820 * Fires when the iframe editor is updated with content from the textarea.
22821 * @param {HtmlEditor} this
22822 * @param {String} html
22826 * @event editmodechange
22827 * Fires when the editor switches edit modes
22828 * @param {HtmlEditor} this
22829 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22831 editmodechange: true,
22833 * @event editorevent
22834 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22835 * @param {HtmlEditor} this
22839 * @event firstfocus
22840 * Fires when on first focus - needed by toolbars..
22841 * @param {HtmlEditor} this
22846 * Auto save the htmlEditor value as a file into Events
22847 * @param {HtmlEditor} this
22851 * @event savedpreview
22852 * preview the saved version of htmlEditor
22853 * @param {HtmlEditor} this
22860 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
22864 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22869 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
22874 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
22879 * @cfg {Number} height (in pixels)
22883 * @cfg {Number} width (in pixels)
22888 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22891 stylesheets: false,
22896 // private properties
22897 validationEvent : false,
22899 initialized : false,
22902 onFocus : Roo.emptyFn,
22904 hideMode:'offsets',
22906 tbContainer : false,
22908 toolbarContainer :function() {
22909 return this.wrap.select('.x-html-editor-tb',true).first();
22913 * Protected method that will not generally be called directly. It
22914 * is called when the editor creates its toolbar. Override this method if you need to
22915 * add custom toolbar buttons.
22916 * @param {HtmlEditor} editor
22918 createToolbar : function(){
22919 Roo.log('renewing');
22920 Roo.log("create toolbars");
22922 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
22923 this.toolbars[0].render(this.toolbarContainer());
22927 // if (!editor.toolbars || !editor.toolbars.length) {
22928 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
22931 // for (var i =0 ; i < editor.toolbars.length;i++) {
22932 // editor.toolbars[i] = Roo.factory(
22933 // typeof(editor.toolbars[i]) == 'string' ?
22934 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
22935 // Roo.bootstrap.HtmlEditor);
22936 // editor.toolbars[i].init(editor);
22942 onRender : function(ct, position)
22944 // Roo.log("Call onRender: " + this.xtype);
22946 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
22948 this.wrap = this.inputEl().wrap({
22949 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22952 this.editorcore.onRender(ct, position);
22954 if (this.resizable) {
22955 this.resizeEl = new Roo.Resizable(this.wrap, {
22959 minHeight : this.height,
22960 height: this.height,
22961 handles : this.resizable,
22964 resize : function(r, w, h) {
22965 _t.onResize(w,h); // -something
22971 this.createToolbar(this);
22974 if(!this.width && this.resizable){
22975 this.setSize(this.wrap.getSize());
22977 if (this.resizeEl) {
22978 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22979 // should trigger onReize..
22985 onResize : function(w, h)
22987 Roo.log('resize: ' +w + ',' + h );
22988 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
22992 if(this.inputEl() ){
22993 if(typeof w == 'number'){
22994 var aw = w - this.wrap.getFrameWidth('lr');
22995 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
22998 if(typeof h == 'number'){
22999 var tbh = -11; // fixme it needs to tool bar size!
23000 for (var i =0; i < this.toolbars.length;i++) {
23001 // fixme - ask toolbars for heights?
23002 tbh += this.toolbars[i].el.getHeight();
23003 //if (this.toolbars[i].footer) {
23004 // tbh += this.toolbars[i].footer.el.getHeight();
23012 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23013 ah -= 5; // knock a few pixes off for look..
23014 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23018 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23019 this.editorcore.onResize(ew,eh);
23024 * Toggles the editor between standard and source edit mode.
23025 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23027 toggleSourceEdit : function(sourceEditMode)
23029 this.editorcore.toggleSourceEdit(sourceEditMode);
23031 if(this.editorcore.sourceEditMode){
23032 Roo.log('editor - showing textarea');
23035 // Roo.log(this.syncValue());
23037 this.inputEl().removeClass(['hide', 'x-hidden']);
23038 this.inputEl().dom.removeAttribute('tabIndex');
23039 this.inputEl().focus();
23041 Roo.log('editor - hiding textarea');
23043 // Roo.log(this.pushValue());
23046 this.inputEl().addClass(['hide', 'x-hidden']);
23047 this.inputEl().dom.setAttribute('tabIndex', -1);
23048 //this.deferFocus();
23051 if(this.resizable){
23052 this.setSize(this.wrap.getSize());
23055 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23058 // private (for BoxComponent)
23059 adjustSize : Roo.BoxComponent.prototype.adjustSize,
23061 // private (for BoxComponent)
23062 getResizeEl : function(){
23066 // private (for BoxComponent)
23067 getPositionEl : function(){
23072 initEvents : function(){
23073 this.originalValue = this.getValue();
23077 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23080 // markInvalid : Roo.emptyFn,
23082 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23085 // clearInvalid : Roo.emptyFn,
23087 setValue : function(v){
23088 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23089 this.editorcore.pushValue();
23094 deferFocus : function(){
23095 this.focus.defer(10, this);
23099 focus : function(){
23100 this.editorcore.focus();
23106 onDestroy : function(){
23112 for (var i =0; i < this.toolbars.length;i++) {
23113 // fixme - ask toolbars for heights?
23114 this.toolbars[i].onDestroy();
23117 this.wrap.dom.innerHTML = '';
23118 this.wrap.remove();
23123 onFirstFocus : function(){
23124 //Roo.log("onFirstFocus");
23125 this.editorcore.onFirstFocus();
23126 for (var i =0; i < this.toolbars.length;i++) {
23127 this.toolbars[i].onFirstFocus();
23133 syncValue : function()
23135 this.editorcore.syncValue();
23138 pushValue : function()
23140 this.editorcore.pushValue();
23144 // hide stuff that is not compatible
23158 * @event specialkey
23162 * @cfg {String} fieldClass @hide
23165 * @cfg {String} focusClass @hide
23168 * @cfg {String} autoCreate @hide
23171 * @cfg {String} inputType @hide
23174 * @cfg {String} invalidClass @hide
23177 * @cfg {String} invalidText @hide
23180 * @cfg {String} msgFx @hide
23183 * @cfg {String} validateOnBlur @hide
23192 Roo.namespace('Roo.bootstrap.htmleditor');
23194 * @class Roo.bootstrap.HtmlEditorToolbar1
23199 new Roo.bootstrap.HtmlEditor({
23202 new Roo.bootstrap.HtmlEditorToolbar1({
23203 disable : { fonts: 1 , format: 1, ..., ... , ...],
23209 * @cfg {Object} disable List of elements to disable..
23210 * @cfg {Array} btns List of additional buttons.
23214 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23217 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23220 Roo.apply(this, config);
23222 // default disabled, based on 'good practice'..
23223 this.disable = this.disable || {};
23224 Roo.applyIf(this.disable, {
23227 specialElements : true
23229 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23231 this.editor = config.editor;
23232 this.editorcore = config.editor.editorcore;
23234 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23236 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23237 // dont call parent... till later.
23239 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
23244 editorcore : false,
23249 "h1","h2","h3","h4","h5","h6",
23251 "abbr", "acronym", "address", "cite", "samp", "var",
23255 onRender : function(ct, position)
23257 // Roo.log("Call onRender: " + this.xtype);
23259 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23261 this.el.dom.style.marginBottom = '0';
23263 var editorcore = this.editorcore;
23264 var editor= this.editor;
23267 var btn = function(id,cmd , toggle, handler, html){
23269 var event = toggle ? 'toggle' : 'click';
23274 xns: Roo.bootstrap,
23277 enableToggle:toggle !== false,
23279 pressed : toggle ? false : null,
23282 a.listeners[toggle ? 'toggle' : 'click'] = function() {
23283 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
23289 // var cb_box = function...
23294 xns: Roo.bootstrap,
23295 glyphicon : 'font',
23299 xns: Roo.bootstrap,
23303 Roo.each(this.formats, function(f) {
23304 style.menu.items.push({
23306 xns: Roo.bootstrap,
23307 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23312 editorcore.insertTag(this.tagname);
23319 children.push(style);
23321 btn('bold',false,true);
23322 btn('italic',false,true);
23323 btn('align-left', 'justifyleft',true);
23324 btn('align-center', 'justifycenter',true);
23325 btn('align-right' , 'justifyright',true);
23326 btn('link', false, false, function(btn) {
23327 //Roo.log("create link?");
23328 var url = prompt(this.createLinkText, this.defaultLinkValue);
23329 if(url && url != 'http:/'+'/'){
23330 this.editorcore.relayCmd('createlink', url);
23333 btn('list','insertunorderedlist',true);
23334 btn('pencil', false,true, function(btn){
23336 this.toggleSourceEdit(btn.pressed);
23339 if (this.editor.btns.length > 0) {
23340 for (var i = 0; i<this.editor.btns.length; i++) {
23341 children.push(this.editor.btns[i]);
23349 xns: Roo.bootstrap,
23354 xns: Roo.bootstrap,
23359 cog.menu.items.push({
23361 xns: Roo.bootstrap,
23362 html : Clean styles,
23367 editorcore.insertTag(this.tagname);
23376 this.xtype = 'NavSimplebar';
23378 for(var i=0;i< children.length;i++) {
23380 this.buttons.add(this.addxtypeChild(children[i]));
23384 editor.on('editorevent', this.updateToolbar, this);
23386 onBtnClick : function(id)
23388 this.editorcore.relayCmd(id);
23389 this.editorcore.focus();
23393 * Protected method that will not generally be called directly. It triggers
23394 * a toolbar update by reading the markup state of the current selection in the editor.
23396 updateToolbar: function(){
23398 if(!this.editorcore.activated){
23399 this.editor.onFirstFocus(); // is this neeed?
23403 var btns = this.buttons;
23404 var doc = this.editorcore.doc;
23405 btns.get('bold').setActive(doc.queryCommandState('bold'));
23406 btns.get('italic').setActive(doc.queryCommandState('italic'));
23407 //btns.get('underline').setActive(doc.queryCommandState('underline'));
23409 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23410 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23411 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23413 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23414 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23417 var ans = this.editorcore.getAllAncestors();
23418 if (this.formatCombo) {
23421 var store = this.formatCombo.store;
23422 this.formatCombo.setValue("");
23423 for (var i =0; i < ans.length;i++) {
23424 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23426 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23434 // hides menus... - so this cant be on a menu...
23435 Roo.bootstrap.MenuMgr.hideAll();
23437 Roo.bootstrap.MenuMgr.hideAll();
23438 //this.editorsyncValue();
23440 onFirstFocus: function() {
23441 this.buttons.each(function(item){
23445 toggleSourceEdit : function(sourceEditMode){
23448 if(sourceEditMode){
23449 Roo.log("disabling buttons");
23450 this.buttons.each( function(item){
23451 if(item.cmd != 'pencil'){
23457 Roo.log("enabling buttons");
23458 if(this.editorcore.initialized){
23459 this.buttons.each( function(item){
23465 Roo.log("calling toggole on editor");
23466 // tell the editor that it's been pressed..
23467 this.editor.toggleSourceEdit(sourceEditMode);
23477 * @class Roo.bootstrap.Table.AbstractSelectionModel
23478 * @extends Roo.util.Observable
23479 * Abstract base class for grid SelectionModels. It provides the interface that should be
23480 * implemented by descendant classes. This class should not be directly instantiated.
23483 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23484 this.locked = false;
23485 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23489 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
23490 /** @ignore Called by the grid automatically. Do not call directly. */
23491 init : function(grid){
23497 * Locks the selections.
23500 this.locked = true;
23504 * Unlocks the selections.
23506 unlock : function(){
23507 this.locked = false;
23511 * Returns true if the selections are locked.
23512 * @return {Boolean}
23514 isLocked : function(){
23515 return this.locked;
23519 * @extends Roo.bootstrap.Table.AbstractSelectionModel
23520 * @class Roo.bootstrap.Table.RowSelectionModel
23521 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23522 * It supports multiple selections and keyboard selection/navigation.
23524 * @param {Object} config
23527 Roo.bootstrap.Table.RowSelectionModel = function(config){
23528 Roo.apply(this, config);
23529 this.selections = new Roo.util.MixedCollection(false, function(o){
23534 this.lastActive = false;
23538 * @event selectionchange
23539 * Fires when the selection changes
23540 * @param {SelectionModel} this
23542 "selectionchange" : true,
23544 * @event afterselectionchange
23545 * Fires after the selection changes (eg. by key press or clicking)
23546 * @param {SelectionModel} this
23548 "afterselectionchange" : true,
23550 * @event beforerowselect
23551 * Fires when a row is selected being selected, return false to cancel.
23552 * @param {SelectionModel} this
23553 * @param {Number} rowIndex The selected index
23554 * @param {Boolean} keepExisting False if other selections will be cleared
23556 "beforerowselect" : true,
23559 * Fires when a row is selected.
23560 * @param {SelectionModel} this
23561 * @param {Number} rowIndex The selected index
23562 * @param {Roo.data.Record} r The record
23564 "rowselect" : true,
23566 * @event rowdeselect
23567 * Fires when a row is deselected.
23568 * @param {SelectionModel} this
23569 * @param {Number} rowIndex The selected index
23571 "rowdeselect" : true
23573 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23574 this.locked = false;
23577 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
23579 * @cfg {Boolean} singleSelect
23580 * True to allow selection of only one row at a time (defaults to false)
23582 singleSelect : false,
23585 initEvents : function()
23588 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
23589 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
23590 //}else{ // allow click to work like normal
23591 // this.grid.on("rowclick", this.handleDragableRowClick, this);
23593 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
23594 this.grid.on("rowclick", this.handleMouseDown, this);
23596 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
23597 "up" : function(e){
23599 this.selectPrevious(e.shiftKey);
23600 }else if(this.last !== false && this.lastActive !== false){
23601 var last = this.last;
23602 this.selectRange(this.last, this.lastActive-1);
23603 this.grid.getView().focusRow(this.lastActive);
23604 if(last !== false){
23608 this.selectFirstRow();
23610 this.fireEvent("afterselectionchange", this);
23612 "down" : function(e){
23614 this.selectNext(e.shiftKey);
23615 }else if(this.last !== false && this.lastActive !== false){
23616 var last = this.last;
23617 this.selectRange(this.last, this.lastActive+1);
23618 this.grid.getView().focusRow(this.lastActive);
23619 if(last !== false){
23623 this.selectFirstRow();
23625 this.fireEvent("afterselectionchange", this);
23629 this.grid.store.on('load', function(){
23630 this.selections.clear();
23633 var view = this.grid.view;
23634 view.on("refresh", this.onRefresh, this);
23635 view.on("rowupdated", this.onRowUpdated, this);
23636 view.on("rowremoved", this.onRemove, this);
23641 onRefresh : function()
23643 var ds = this.grid.store, i, v = this.grid.view;
23644 var s = this.selections;
23645 s.each(function(r){
23646 if((i = ds.indexOfId(r.id)) != -1){
23655 onRemove : function(v, index, r){
23656 this.selections.remove(r);
23660 onRowUpdated : function(v, index, r){
23661 if(this.isSelected(r)){
23662 v.onRowSelect(index);
23668 * @param {Array} records The records to select
23669 * @param {Boolean} keepExisting (optional) True to keep existing selections
23671 selectRecords : function(records, keepExisting)
23674 this.clearSelections();
23676 var ds = this.grid.store;
23677 for(var i = 0, len = records.length; i < len; i++){
23678 this.selectRow(ds.indexOf(records[i]), true);
23683 * Gets the number of selected rows.
23686 getCount : function(){
23687 return this.selections.length;
23691 * Selects the first row in the grid.
23693 selectFirstRow : function(){
23698 * Select the last row.
23699 * @param {Boolean} keepExisting (optional) True to keep existing selections
23701 selectLastRow : function(keepExisting){
23702 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
23703 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
23707 * Selects the row immediately following the last selected row.
23708 * @param {Boolean} keepExisting (optional) True to keep existing selections
23710 selectNext : function(keepExisting)
23712 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
23713 this.selectRow(this.last+1, keepExisting);
23714 this.grid.getView().focusRow(this.last);
23719 * Selects the row that precedes the last selected row.
23720 * @param {Boolean} keepExisting (optional) True to keep existing selections
23722 selectPrevious : function(keepExisting){
23724 this.selectRow(this.last-1, keepExisting);
23725 this.grid.getView().focusRow(this.last);
23730 * Returns the selected records
23731 * @return {Array} Array of selected records
23733 getSelections : function(){
23734 return [].concat(this.selections.items);
23738 * Returns the first selected record.
23741 getSelected : function(){
23742 return this.selections.itemAt(0);
23747 * Clears all selections.
23749 clearSelections : function(fast)
23755 var ds = this.grid.store;
23756 var s = this.selections;
23757 s.each(function(r){
23758 this.deselectRow(ds.indexOfId(r.id));
23762 this.selections.clear();
23769 * Selects all rows.
23771 selectAll : function(){
23775 this.selections.clear();
23776 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
23777 this.selectRow(i, true);
23782 * Returns True if there is a selection.
23783 * @return {Boolean}
23785 hasSelection : function(){
23786 return this.selections.length > 0;
23790 * Returns True if the specified row is selected.
23791 * @param {Number/Record} record The record or index of the record to check
23792 * @return {Boolean}
23794 isSelected : function(index){
23795 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
23796 return (r && this.selections.key(r.id) ? true : false);
23800 * Returns True if the specified record id is selected.
23801 * @param {String} id The id of record to check
23802 * @return {Boolean}
23804 isIdSelected : function(id){
23805 return (this.selections.key(id) ? true : false);
23810 handleMouseDBClick : function(e, t){
23814 handleMouseDown : function(e, t)
23816 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
23817 if(this.isLocked() || rowIndex < 0 ){
23820 if(e.shiftKey && this.last !== false){
23821 var last = this.last;
23822 this.selectRange(last, rowIndex, e.ctrlKey);
23823 this.last = last; // reset the last
23827 var isSelected = this.isSelected(rowIndex);
23828 //Roo.log("select row:" + rowIndex);
23830 this.deselectRow(rowIndex);
23832 this.selectRow(rowIndex, true);
23836 if(e.button !== 0 && isSelected){
23837 alert('rowIndex 2: ' + rowIndex);
23838 view.focusRow(rowIndex);
23839 }else if(e.ctrlKey && isSelected){
23840 this.deselectRow(rowIndex);
23841 }else if(!isSelected){
23842 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
23843 view.focusRow(rowIndex);
23847 this.fireEvent("afterselectionchange", this);
23850 handleDragableRowClick : function(grid, rowIndex, e)
23852 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
23853 this.selectRow(rowIndex, false);
23854 grid.view.focusRow(rowIndex);
23855 this.fireEvent("afterselectionchange", this);
23860 * Selects multiple rows.
23861 * @param {Array} rows Array of the indexes of the row to select
23862 * @param {Boolean} keepExisting (optional) True to keep existing selections
23864 selectRows : function(rows, keepExisting){
23866 this.clearSelections();
23868 for(var i = 0, len = rows.length; i < len; i++){
23869 this.selectRow(rows[i], true);
23874 * Selects a range of rows. All rows in between startRow and endRow are also selected.
23875 * @param {Number} startRow The index of the first row in the range
23876 * @param {Number} endRow The index of the last row in the range
23877 * @param {Boolean} keepExisting (optional) True to retain existing selections
23879 selectRange : function(startRow, endRow, keepExisting){
23884 this.clearSelections();
23886 if(startRow <= endRow){
23887 for(var i = startRow; i <= endRow; i++){
23888 this.selectRow(i, true);
23891 for(var i = startRow; i >= endRow; i--){
23892 this.selectRow(i, true);
23898 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
23899 * @param {Number} startRow The index of the first row in the range
23900 * @param {Number} endRow The index of the last row in the range
23902 deselectRange : function(startRow, endRow, preventViewNotify){
23906 for(var i = startRow; i <= endRow; i++){
23907 this.deselectRow(i, preventViewNotify);
23913 * @param {Number} row The index of the row to select
23914 * @param {Boolean} keepExisting (optional) True to keep existing selections
23916 selectRow : function(index, keepExisting, preventViewNotify)
23918 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
23921 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
23922 if(!keepExisting || this.singleSelect){
23923 this.clearSelections();
23926 var r = this.grid.store.getAt(index);
23927 //console.log('selectRow - record id :' + r.id);
23929 this.selections.add(r);
23930 this.last = this.lastActive = index;
23931 if(!preventViewNotify){
23932 var proxy = new Roo.Element(
23933 this.grid.getRowDom(index)
23935 proxy.addClass('bg-info info');
23937 this.fireEvent("rowselect", this, index, r);
23938 this.fireEvent("selectionchange", this);
23944 * @param {Number} row The index of the row to deselect
23946 deselectRow : function(index, preventViewNotify)
23951 if(this.last == index){
23954 if(this.lastActive == index){
23955 this.lastActive = false;
23958 var r = this.grid.store.getAt(index);
23963 this.selections.remove(r);
23964 //.console.log('deselectRow - record id :' + r.id);
23965 if(!preventViewNotify){
23967 var proxy = new Roo.Element(
23968 this.grid.getRowDom(index)
23970 proxy.removeClass('bg-info info');
23972 this.fireEvent("rowdeselect", this, index);
23973 this.fireEvent("selectionchange", this);
23977 restoreLast : function(){
23979 this.last = this._last;
23984 acceptsNav : function(row, col, cm){
23985 return !cm.isHidden(col) && cm.isCellEditable(col, row);
23989 onEditorKey : function(field, e){
23990 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
23995 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
23997 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
23999 }else if(k == e.ENTER && !e.ctrlKey){
24003 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24005 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24007 }else if(k == e.ESC){
24011 g.startEditing(newCell[0], newCell[1]);
24017 * Ext JS Library 1.1.1
24018 * Copyright(c) 2006-2007, Ext JS, LLC.
24020 * Originally Released Under LGPL - original licence link has changed is not relivant.
24023 * <script type="text/javascript">
24027 * @class Roo.bootstrap.PagingToolbar
24028 * @extends Roo.bootstrap.NavSimplebar
24029 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24031 * Create a new PagingToolbar
24032 * @param {Object} config The config object
24033 * @param {Roo.data.Store} store
24035 Roo.bootstrap.PagingToolbar = function(config)
24037 // old args format still supported... - xtype is prefered..
24038 // created from xtype...
24040 this.ds = config.dataSource;
24042 if (config.store && !this.ds) {
24043 this.store= Roo.factory(config.store, Roo.data);
24044 this.ds = this.store;
24045 this.ds.xmodule = this.xmodule || false;
24048 this.toolbarItems = [];
24049 if (config.items) {
24050 this.toolbarItems = config.items;
24053 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24058 this.bind(this.ds);
24061 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24065 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24067 * @cfg {Roo.data.Store} dataSource
24068 * The underlying data store providing the paged data
24071 * @cfg {String/HTMLElement/Element} container
24072 * container The id or element that will contain the toolbar
24075 * @cfg {Boolean} displayInfo
24076 * True to display the displayMsg (defaults to false)
24079 * @cfg {Number} pageSize
24080 * The number of records to display per page (defaults to 20)
24084 * @cfg {String} displayMsg
24085 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24087 displayMsg : 'Displaying {0} - {1} of {2}',
24089 * @cfg {String} emptyMsg
24090 * The message to display when no records are found (defaults to "No data to display")
24092 emptyMsg : 'No data to display',
24094 * Customizable piece of the default paging text (defaults to "Page")
24097 beforePageText : "Page",
24099 * Customizable piece of the default paging text (defaults to "of %0")
24102 afterPageText : "of {0}",
24104 * Customizable piece of the default paging text (defaults to "First Page")
24107 firstText : "First Page",
24109 * Customizable piece of the default paging text (defaults to "Previous Page")
24112 prevText : "Previous Page",
24114 * Customizable piece of the default paging text (defaults to "Next Page")
24117 nextText : "Next Page",
24119 * Customizable piece of the default paging text (defaults to "Last Page")
24122 lastText : "Last Page",
24124 * Customizable piece of the default paging text (defaults to "Refresh")
24127 refreshText : "Refresh",
24131 onRender : function(ct, position)
24133 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24134 this.navgroup.parentId = this.id;
24135 this.navgroup.onRender(this.el, null);
24136 // add the buttons to the navgroup
24138 if(this.displayInfo){
24139 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24140 this.displayEl = this.el.select('.x-paging-info', true).first();
24141 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24142 // this.displayEl = navel.el.select('span',true).first();
24148 Roo.each(_this.buttons, function(e){ // this might need to use render????
24149 Roo.factory(e).onRender(_this.el, null);
24153 Roo.each(_this.toolbarItems, function(e) {
24154 _this.navgroup.addItem(e);
24158 this.first = this.navgroup.addItem({
24159 tooltip: this.firstText,
24161 icon : 'fa fa-backward',
24163 preventDefault: true,
24164 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24167 this.prev = this.navgroup.addItem({
24168 tooltip: this.prevText,
24170 icon : 'fa fa-step-backward',
24172 preventDefault: true,
24173 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
24175 //this.addSeparator();
24178 var field = this.navgroup.addItem( {
24180 cls : 'x-paging-position',
24182 html : this.beforePageText +
24183 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24184 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
24187 this.field = field.el.select('input', true).first();
24188 this.field.on("keydown", this.onPagingKeydown, this);
24189 this.field.on("focus", function(){this.dom.select();});
24192 this.afterTextEl = field.el.select('.x-paging-after',true).first();
24193 //this.field.setHeight(18);
24194 //this.addSeparator();
24195 this.next = this.navgroup.addItem({
24196 tooltip: this.nextText,
24198 html : ' <i class="fa fa-step-forward">',
24200 preventDefault: true,
24201 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
24203 this.last = this.navgroup.addItem({
24204 tooltip: this.lastText,
24205 icon : 'fa fa-forward',
24208 preventDefault: true,
24209 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
24211 //this.addSeparator();
24212 this.loading = this.navgroup.addItem({
24213 tooltip: this.refreshText,
24214 icon: 'fa fa-refresh',
24215 preventDefault: true,
24216 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24222 updateInfo : function(){
24223 if(this.displayEl){
24224 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24225 var msg = count == 0 ?
24229 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
24231 this.displayEl.update(msg);
24236 onLoad : function(ds, r, o)
24238 this.cursor = o.params ? o.params.start : 0;
24239 var d = this.getPageData(),
24244 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24245 this.field.dom.value = ap;
24246 this.first.setDisabled(ap == 1);
24247 this.prev.setDisabled(ap == 1);
24248 this.next.setDisabled(ap == ps);
24249 this.last.setDisabled(ap == ps);
24250 this.loading.enable();
24255 getPageData : function(){
24256 var total = this.ds.getTotalCount();
24259 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24260 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24265 onLoadError : function(){
24266 this.loading.enable();
24270 onPagingKeydown : function(e){
24271 var k = e.getKey();
24272 var d = this.getPageData();
24274 var v = this.field.dom.value, pageNum;
24275 if(!v || isNaN(pageNum = parseInt(v, 10))){
24276 this.field.dom.value = d.activePage;
24279 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24280 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24283 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))
24285 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24286 this.field.dom.value = pageNum;
24287 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24290 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24292 var v = this.field.dom.value, pageNum;
24293 var increment = (e.shiftKey) ? 10 : 1;
24294 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24297 if(!v || isNaN(pageNum = parseInt(v, 10))) {
24298 this.field.dom.value = d.activePage;
24301 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24303 this.field.dom.value = parseInt(v, 10) + increment;
24304 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24305 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24312 beforeLoad : function(){
24314 this.loading.disable();
24319 onClick : function(which){
24328 ds.load({params:{start: 0, limit: this.pageSize}});
24331 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24334 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24337 var total = ds.getTotalCount();
24338 var extra = total % this.pageSize;
24339 var lastStart = extra ? (total - extra) : total-this.pageSize;
24340 ds.load({params:{start: lastStart, limit: this.pageSize}});
24343 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24349 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24350 * @param {Roo.data.Store} store The data store to unbind
24352 unbind : function(ds){
24353 ds.un("beforeload", this.beforeLoad, this);
24354 ds.un("load", this.onLoad, this);
24355 ds.un("loadexception", this.onLoadError, this);
24356 ds.un("remove", this.updateInfo, this);
24357 ds.un("add", this.updateInfo, this);
24358 this.ds = undefined;
24362 * Binds the paging toolbar to the specified {@link Roo.data.Store}
24363 * @param {Roo.data.Store} store The data store to bind
24365 bind : function(ds){
24366 ds.on("beforeload", this.beforeLoad, this);
24367 ds.on("load", this.onLoad, this);
24368 ds.on("loadexception", this.onLoadError, this);
24369 ds.on("remove", this.updateInfo, this);
24370 ds.on("add", this.updateInfo, this);
24381 * @class Roo.bootstrap.MessageBar
24382 * @extends Roo.bootstrap.Component
24383 * Bootstrap MessageBar class
24384 * @cfg {String} html contents of the MessageBar
24385 * @cfg {String} weight (info | success | warning | danger) default info
24386 * @cfg {String} beforeClass insert the bar before the given class
24387 * @cfg {Boolean} closable (true | false) default false
24388 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24391 * Create a new Element
24392 * @param {Object} config The config object
24395 Roo.bootstrap.MessageBar = function(config){
24396 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24399 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
24405 beforeClass: 'bootstrap-sticky-wrap',
24407 getAutoCreate : function(){
24411 cls: 'alert alert-dismissable alert-' + this.weight,
24416 html: this.html || ''
24422 cfg.cls += ' alert-messages-fixed';
24436 onRender : function(ct, position)
24438 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24441 var cfg = Roo.apply({}, this.getAutoCreate());
24445 cfg.cls += ' ' + this.cls;
24448 cfg.style = this.style;
24450 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24452 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24455 this.el.select('>button.close').on('click', this.hide, this);
24461 if (!this.rendered) {
24467 this.fireEvent('show', this);
24473 if (!this.rendered) {
24479 this.fireEvent('hide', this);
24482 update : function()
24484 // var e = this.el.dom.firstChild;
24486 // if(this.closable){
24487 // e = e.nextSibling;
24490 // e.data = this.html || '';
24492 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24508 * @class Roo.bootstrap.Graph
24509 * @extends Roo.bootstrap.Component
24510 * Bootstrap Graph class
24514 @cfg {String} graphtype bar | vbar | pie
24515 @cfg {number} g_x coodinator | centre x (pie)
24516 @cfg {number} g_y coodinator | centre y (pie)
24517 @cfg {number} g_r radius (pie)
24518 @cfg {number} g_height height of the chart (respected by all elements in the set)
24519 @cfg {number} g_width width of the chart (respected by all elements in the set)
24520 @cfg {Object} title The title of the chart
24523 -opts (object) options for the chart
24525 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24526 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24528 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.
24529 o stacked (boolean) whether or not to tread values as in a stacked bar chart
24531 o stretch (boolean)
24533 -opts (object) options for the pie
24536 o startAngle (number)
24537 o endAngle (number)
24541 * Create a new Input
24542 * @param {Object} config The config object
24545 Roo.bootstrap.Graph = function(config){
24546 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24552 * The img click event for the img.
24553 * @param {Roo.EventObject} e
24559 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
24570 //g_colors: this.colors,
24577 getAutoCreate : function(){
24588 onRender : function(ct,position){
24591 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
24593 if (typeof(Raphael) == 'undefined') {
24594 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
24598 this.raphael = Raphael(this.el.dom);
24600 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24601 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24602 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24603 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
24605 r.text(160, 10, "Single Series Chart").attr(txtattr);
24606 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
24607 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
24608 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
24610 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
24611 r.barchart(330, 10, 300, 220, data1);
24612 r.barchart(10, 250, 300, 220, data2, {stacked: true});
24613 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
24616 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24617 // r.barchart(30, 30, 560, 250, xdata, {
24618 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
24619 // axis : "0 0 1 1",
24620 // axisxlabels : xdata
24621 // //yvalues : cols,
24624 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24626 // this.load(null,xdata,{
24627 // axis : "0 0 1 1",
24628 // axisxlabels : xdata
24633 load : function(graphtype,xdata,opts)
24635 this.raphael.clear();
24637 graphtype = this.graphtype;
24642 var r = this.raphael,
24643 fin = function () {
24644 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
24646 fout = function () {
24647 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
24649 pfin = function() {
24650 this.sector.stop();
24651 this.sector.scale(1.1, 1.1, this.cx, this.cy);
24654 this.label[0].stop();
24655 this.label[0].attr({ r: 7.5 });
24656 this.label[1].attr({ "font-weight": 800 });
24659 pfout = function() {
24660 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
24663 this.label[0].animate({ r: 5 }, 500, "bounce");
24664 this.label[1].attr({ "font-weight": 400 });
24670 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24673 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24676 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
24677 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
24679 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
24686 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
24691 setTitle: function(o)
24696 initEvents: function() {
24699 this.el.on('click', this.onClick, this);
24703 onClick : function(e)
24705 Roo.log('img onclick');
24706 this.fireEvent('click', this, e);
24718 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24721 * @class Roo.bootstrap.dash.NumberBox
24722 * @extends Roo.bootstrap.Component
24723 * Bootstrap NumberBox class
24724 * @cfg {String} headline Box headline
24725 * @cfg {String} content Box content
24726 * @cfg {String} icon Box icon
24727 * @cfg {String} footer Footer text
24728 * @cfg {String} fhref Footer href
24731 * Create a new NumberBox
24732 * @param {Object} config The config object
24736 Roo.bootstrap.dash.NumberBox = function(config){
24737 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
24741 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
24750 getAutoCreate : function(){
24754 cls : 'small-box ',
24762 cls : 'roo-headline',
24763 html : this.headline
24767 cls : 'roo-content',
24768 html : this.content
24782 cls : 'ion ' + this.icon
24791 cls : 'small-box-footer',
24792 href : this.fhref || '#',
24796 cfg.cn.push(footer);
24803 onRender : function(ct,position){
24804 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
24811 setHeadline: function (value)
24813 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
24816 setFooter: function (value, href)
24818 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
24821 this.el.select('a.small-box-footer',true).first().attr('href', href);
24826 setContent: function (value)
24828 this.el.select('.roo-content',true).first().dom.innerHTML = value;
24831 initEvents: function()
24845 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24848 * @class Roo.bootstrap.dash.TabBox
24849 * @extends Roo.bootstrap.Component
24850 * Bootstrap TabBox class
24851 * @cfg {String} title Title of the TabBox
24852 * @cfg {String} icon Icon of the TabBox
24853 * @cfg {Boolean} showtabs (true|false) show the tabs default true
24854 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
24857 * Create a new TabBox
24858 * @param {Object} config The config object
24862 Roo.bootstrap.dash.TabBox = function(config){
24863 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
24868 * When a pane is added
24869 * @param {Roo.bootstrap.dash.TabPane} pane
24873 * @event activatepane
24874 * When a pane is activated
24875 * @param {Roo.bootstrap.dash.TabPane} pane
24877 "activatepane" : true
24885 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
24890 tabScrollable : false,
24892 getChildContainer : function()
24894 return this.el.select('.tab-content', true).first();
24897 getAutoCreate : function(){
24901 cls: 'pull-left header',
24909 cls: 'fa ' + this.icon
24915 cls: 'nav nav-tabs pull-right',
24921 if(this.tabScrollable){
24928 cls: 'nav nav-tabs pull-right',
24939 cls: 'nav-tabs-custom',
24944 cls: 'tab-content no-padding',
24952 initEvents : function()
24954 //Roo.log('add add pane handler');
24955 this.on('addpane', this.onAddPane, this);
24958 * Updates the box title
24959 * @param {String} html to set the title to.
24961 setTitle : function(value)
24963 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
24965 onAddPane : function(pane)
24967 this.panes.push(pane);
24968 //Roo.log('addpane');
24970 // tabs are rendere left to right..
24971 if(!this.showtabs){
24975 var ctr = this.el.select('.nav-tabs', true).first();
24978 var existing = ctr.select('.nav-tab',true);
24979 var qty = existing.getCount();;
24982 var tab = ctr.createChild({
24984 cls : 'nav-tab' + (qty ? '' : ' active'),
24992 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
24995 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
24997 pane.el.addClass('active');
25002 onTabClick : function(ev,un,ob,pane)
25004 //Roo.log('tab - prev default');
25005 ev.preventDefault();
25008 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25009 pane.tab.addClass('active');
25010 //Roo.log(pane.title);
25011 this.getChildContainer().select('.tab-pane',true).removeClass('active');
25012 // technically we should have a deactivate event.. but maybe add later.
25013 // and it should not de-activate the selected tab...
25014 this.fireEvent('activatepane', pane);
25015 pane.el.addClass('active');
25016 pane.fireEvent('activate');
25021 getActivePane : function()
25024 Roo.each(this.panes, function(p) {
25025 if(p.el.hasClass('active')){
25046 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25048 * @class Roo.bootstrap.TabPane
25049 * @extends Roo.bootstrap.Component
25050 * Bootstrap TabPane class
25051 * @cfg {Boolean} active (false | true) Default false
25052 * @cfg {String} title title of panel
25056 * Create a new TabPane
25057 * @param {Object} config The config object
25060 Roo.bootstrap.dash.TabPane = function(config){
25061 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25067 * When a pane is activated
25068 * @param {Roo.bootstrap.dash.TabPane} pane
25075 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
25080 // the tabBox that this is attached to.
25083 getAutoCreate : function()
25091 cfg.cls += ' active';
25096 initEvents : function()
25098 //Roo.log('trigger add pane handler');
25099 this.parent().fireEvent('addpane', this)
25103 * Updates the tab title
25104 * @param {String} html to set the title to.
25106 setTitle: function(str)
25112 this.tab.select('a', true).first().dom.innerHTML = str;
25129 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25132 * @class Roo.bootstrap.menu.Menu
25133 * @extends Roo.bootstrap.Component
25134 * Bootstrap Menu class - container for Menu
25135 * @cfg {String} html Text of the menu
25136 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25137 * @cfg {String} icon Font awesome icon
25138 * @cfg {String} pos Menu align to (top | bottom) default bottom
25142 * Create a new Menu
25143 * @param {Object} config The config object
25147 Roo.bootstrap.menu.Menu = function(config){
25148 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25152 * @event beforeshow
25153 * Fires before this menu is displayed
25154 * @param {Roo.bootstrap.menu.Menu} this
25158 * @event beforehide
25159 * Fires before this menu is hidden
25160 * @param {Roo.bootstrap.menu.Menu} this
25165 * Fires after this menu is displayed
25166 * @param {Roo.bootstrap.menu.Menu} this
25171 * Fires after this menu is hidden
25172 * @param {Roo.bootstrap.menu.Menu} this
25177 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25178 * @param {Roo.bootstrap.menu.Menu} this
25179 * @param {Roo.EventObject} e
25186 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
25190 weight : 'default',
25195 getChildContainer : function() {
25196 if(this.isSubMenu){
25200 return this.el.select('ul.dropdown-menu', true).first();
25203 getAutoCreate : function()
25208 cls : 'roo-menu-text',
25216 cls : 'fa ' + this.icon
25227 cls : 'dropdown-button btn btn-' + this.weight,
25232 cls : 'dropdown-toggle btn btn-' + this.weight,
25242 cls : 'dropdown-menu'
25248 if(this.pos == 'top'){
25249 cfg.cls += ' dropup';
25252 if(this.isSubMenu){
25255 cls : 'dropdown-menu'
25262 onRender : function(ct, position)
25264 this.isSubMenu = ct.hasClass('dropdown-submenu');
25266 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25269 initEvents : function()
25271 if(this.isSubMenu){
25275 this.hidden = true;
25277 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25278 this.triggerEl.on('click', this.onTriggerPress, this);
25280 this.buttonEl = this.el.select('button.dropdown-button', true).first();
25281 this.buttonEl.on('click', this.onClick, this);
25287 if(this.isSubMenu){
25291 return this.el.select('ul.dropdown-menu', true).first();
25294 onClick : function(e)
25296 this.fireEvent("click", this, e);
25299 onTriggerPress : function(e)
25301 if (this.isVisible()) {
25308 isVisible : function(){
25309 return !this.hidden;
25314 this.fireEvent("beforeshow", this);
25316 this.hidden = false;
25317 this.el.addClass('open');
25319 Roo.get(document).on("mouseup", this.onMouseUp, this);
25321 this.fireEvent("show", this);
25328 this.fireEvent("beforehide", this);
25330 this.hidden = true;
25331 this.el.removeClass('open');
25333 Roo.get(document).un("mouseup", this.onMouseUp);
25335 this.fireEvent("hide", this);
25338 onMouseUp : function()
25352 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25355 * @class Roo.bootstrap.menu.Item
25356 * @extends Roo.bootstrap.Component
25357 * Bootstrap MenuItem class
25358 * @cfg {Boolean} submenu (true | false) default false
25359 * @cfg {String} html text of the item
25360 * @cfg {String} href the link
25361 * @cfg {Boolean} disable (true | false) default false
25362 * @cfg {Boolean} preventDefault (true | false) default true
25363 * @cfg {String} icon Font awesome icon
25364 * @cfg {String} pos Submenu align to (left | right) default right
25368 * Create a new Item
25369 * @param {Object} config The config object
25373 Roo.bootstrap.menu.Item = function(config){
25374 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25378 * Fires when the mouse is hovering over this menu
25379 * @param {Roo.bootstrap.menu.Item} this
25380 * @param {Roo.EventObject} e
25385 * Fires when the mouse exits this menu
25386 * @param {Roo.bootstrap.menu.Item} this
25387 * @param {Roo.EventObject} e
25393 * The raw click event for the entire grid.
25394 * @param {Roo.EventObject} e
25400 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
25405 preventDefault: true,
25410 getAutoCreate : function()
25415 cls : 'roo-menu-item-text',
25423 cls : 'fa ' + this.icon
25432 href : this.href || '#',
25439 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25443 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25445 if(this.pos == 'left'){
25446 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25453 initEvents : function()
25455 this.el.on('mouseover', this.onMouseOver, this);
25456 this.el.on('mouseout', this.onMouseOut, this);
25458 this.el.select('a', true).first().on('click', this.onClick, this);
25462 onClick : function(e)
25464 if(this.preventDefault){
25465 e.preventDefault();
25468 this.fireEvent("click", this, e);
25471 onMouseOver : function(e)
25473 if(this.submenu && this.pos == 'left'){
25474 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25477 this.fireEvent("mouseover", this, e);
25480 onMouseOut : function(e)
25482 this.fireEvent("mouseout", this, e);
25494 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25497 * @class Roo.bootstrap.menu.Separator
25498 * @extends Roo.bootstrap.Component
25499 * Bootstrap Separator class
25502 * Create a new Separator
25503 * @param {Object} config The config object
25507 Roo.bootstrap.menu.Separator = function(config){
25508 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25511 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
25513 getAutoCreate : function(){
25534 * @class Roo.bootstrap.Tooltip
25535 * Bootstrap Tooltip class
25536 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25537 * to determine which dom element triggers the tooltip.
25539 * It needs to add support for additional attributes like tooltip-position
25542 * Create a new Toolti
25543 * @param {Object} config The config object
25546 Roo.bootstrap.Tooltip = function(config){
25547 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25549 this.alignment = Roo.bootstrap.Tooltip.alignment;
25551 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25552 this.alignment = config.alignment;
25557 Roo.apply(Roo.bootstrap.Tooltip, {
25559 * @function init initialize tooltip monitoring.
25563 currentTip : false,
25564 currentRegion : false,
25570 Roo.get(document).on('mouseover', this.enter ,this);
25571 Roo.get(document).on('mouseout', this.leave, this);
25574 this.currentTip = new Roo.bootstrap.Tooltip();
25577 enter : function(ev)
25579 var dom = ev.getTarget();
25581 //Roo.log(['enter',dom]);
25582 var el = Roo.fly(dom);
25583 if (this.currentEl) {
25585 //Roo.log(this.currentEl);
25586 //Roo.log(this.currentEl.contains(dom));
25587 if (this.currentEl == el) {
25590 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
25596 if (this.currentTip.el) {
25597 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
25601 if(!el || el.dom == document){
25607 // you can not look for children, as if el is the body.. then everythign is the child..
25608 if (!el.attr('tooltip')) { //
25609 if (!el.select("[tooltip]").elements.length) {
25612 // is the mouse over this child...?
25613 bindEl = el.select("[tooltip]").first();
25614 var xy = ev.getXY();
25615 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
25616 //Roo.log("not in region.");
25619 //Roo.log("child element over..");
25622 this.currentEl = bindEl;
25623 this.currentTip.bind(bindEl);
25624 this.currentRegion = Roo.lib.Region.getRegion(dom);
25625 this.currentTip.enter();
25628 leave : function(ev)
25630 var dom = ev.getTarget();
25631 //Roo.log(['leave',dom]);
25632 if (!this.currentEl) {
25637 if (dom != this.currentEl.dom) {
25640 var xy = ev.getXY();
25641 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
25644 // only activate leave if mouse cursor is outside... bounding box..
25649 if (this.currentTip) {
25650 this.currentTip.leave();
25652 //Roo.log('clear currentEl');
25653 this.currentEl = false;
25658 'left' : ['r-l', [-2,0], 'right'],
25659 'right' : ['l-r', [2,0], 'left'],
25660 'bottom' : ['t-b', [0,2], 'top'],
25661 'top' : [ 'b-t', [0,-2], 'bottom']
25667 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
25672 delay : null, // can be { show : 300 , hide: 500}
25676 hoverState : null, //???
25678 placement : 'bottom',
25682 getAutoCreate : function(){
25689 cls : 'tooltip-arrow'
25692 cls : 'tooltip-inner'
25699 bind : function(el)
25705 enter : function () {
25707 if (this.timeout != null) {
25708 clearTimeout(this.timeout);
25711 this.hoverState = 'in';
25712 //Roo.log("enter - show");
25713 if (!this.delay || !this.delay.show) {
25718 this.timeout = setTimeout(function () {
25719 if (_t.hoverState == 'in') {
25722 }, this.delay.show);
25726 clearTimeout(this.timeout);
25728 this.hoverState = 'out';
25729 if (!this.delay || !this.delay.hide) {
25735 this.timeout = setTimeout(function () {
25736 //Roo.log("leave - timeout");
25738 if (_t.hoverState == 'out') {
25740 Roo.bootstrap.Tooltip.currentEl = false;
25745 show : function (msg)
25748 this.render(document.body);
25751 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
25753 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
25755 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
25757 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
25759 var placement = typeof this.placement == 'function' ?
25760 this.placement.call(this, this.el, on_el) :
25763 var autoToken = /\s?auto?\s?/i;
25764 var autoPlace = autoToken.test(placement);
25766 placement = placement.replace(autoToken, '') || 'top';
25770 //this.el.setXY([0,0]);
25772 //this.el.dom.style.display='block';
25774 //this.el.appendTo(on_el);
25776 var p = this.getPosition();
25777 var box = this.el.getBox();
25783 var align = this.alignment[placement];
25785 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
25787 if(placement == 'top' || placement == 'bottom'){
25789 placement = 'right';
25792 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
25793 placement = 'left';
25796 var scroll = Roo.select('body', true).first().getScroll();
25798 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
25804 this.el.alignTo(this.bindEl, align[0],align[1]);
25805 //var arrow = this.el.select('.arrow',true).first();
25806 //arrow.set(align[2],
25808 this.el.addClass(placement);
25810 this.el.addClass('in fade');
25812 this.hoverState = null;
25814 if (this.el.hasClass('fade')) {
25825 //this.el.setXY([0,0]);
25826 this.el.removeClass('in');
25842 * @class Roo.bootstrap.LocationPicker
25843 * @extends Roo.bootstrap.Component
25844 * Bootstrap LocationPicker class
25845 * @cfg {Number} latitude Position when init default 0
25846 * @cfg {Number} longitude Position when init default 0
25847 * @cfg {Number} zoom default 15
25848 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
25849 * @cfg {Boolean} mapTypeControl default false
25850 * @cfg {Boolean} disableDoubleClickZoom default false
25851 * @cfg {Boolean} scrollwheel default true
25852 * @cfg {Boolean} streetViewControl default false
25853 * @cfg {Number} radius default 0
25854 * @cfg {String} locationName
25855 * @cfg {Boolean} draggable default true
25856 * @cfg {Boolean} enableAutocomplete default false
25857 * @cfg {Boolean} enableReverseGeocode default true
25858 * @cfg {String} markerTitle
25861 * Create a new LocationPicker
25862 * @param {Object} config The config object
25866 Roo.bootstrap.LocationPicker = function(config){
25868 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
25873 * Fires when the picker initialized.
25874 * @param {Roo.bootstrap.LocationPicker} this
25875 * @param {Google Location} location
25879 * @event positionchanged
25880 * Fires when the picker position changed.
25881 * @param {Roo.bootstrap.LocationPicker} this
25882 * @param {Google Location} location
25884 positionchanged : true,
25887 * Fires when the map resize.
25888 * @param {Roo.bootstrap.LocationPicker} this
25893 * Fires when the map show.
25894 * @param {Roo.bootstrap.LocationPicker} this
25899 * Fires when the map hide.
25900 * @param {Roo.bootstrap.LocationPicker} this
25905 * Fires when click the map.
25906 * @param {Roo.bootstrap.LocationPicker} this
25907 * @param {Map event} e
25911 * @event mapRightClick
25912 * Fires when right click the map.
25913 * @param {Roo.bootstrap.LocationPicker} this
25914 * @param {Map event} e
25916 mapRightClick : true,
25918 * @event markerClick
25919 * Fires when click the marker.
25920 * @param {Roo.bootstrap.LocationPicker} this
25921 * @param {Map event} e
25923 markerClick : true,
25925 * @event markerRightClick
25926 * Fires when right click the marker.
25927 * @param {Roo.bootstrap.LocationPicker} this
25928 * @param {Map event} e
25930 markerRightClick : true,
25932 * @event OverlayViewDraw
25933 * Fires when OverlayView Draw
25934 * @param {Roo.bootstrap.LocationPicker} this
25936 OverlayViewDraw : true,
25938 * @event OverlayViewOnAdd
25939 * Fires when OverlayView Draw
25940 * @param {Roo.bootstrap.LocationPicker} this
25942 OverlayViewOnAdd : true,
25944 * @event OverlayViewOnRemove
25945 * Fires when OverlayView Draw
25946 * @param {Roo.bootstrap.LocationPicker} this
25948 OverlayViewOnRemove : true,
25950 * @event OverlayViewShow
25951 * Fires when OverlayView Draw
25952 * @param {Roo.bootstrap.LocationPicker} this
25953 * @param {Pixel} cpx
25955 OverlayViewShow : true,
25957 * @event OverlayViewHide
25958 * Fires when OverlayView Draw
25959 * @param {Roo.bootstrap.LocationPicker} this
25961 OverlayViewHide : true,
25963 * @event loadexception
25964 * Fires when load google lib failed.
25965 * @param {Roo.bootstrap.LocationPicker} this
25967 loadexception : true
25972 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
25974 gMapContext: false,
25980 mapTypeControl: false,
25981 disableDoubleClickZoom: false,
25983 streetViewControl: false,
25987 enableAutocomplete: false,
25988 enableReverseGeocode: true,
25991 getAutoCreate: function()
25996 cls: 'roo-location-picker'
26002 initEvents: function(ct, position)
26004 if(!this.el.getWidth() || this.isApplied()){
26008 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26013 initial: function()
26015 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26016 this.fireEvent('loadexception', this);
26020 if(!this.mapTypeId){
26021 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26024 this.gMapContext = this.GMapContext();
26026 this.initOverlayView();
26028 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26032 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26033 _this.setPosition(_this.gMapContext.marker.position);
26036 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26037 _this.fireEvent('mapClick', this, event);
26041 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26042 _this.fireEvent('mapRightClick', this, event);
26046 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26047 _this.fireEvent('markerClick', this, event);
26051 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26052 _this.fireEvent('markerRightClick', this, event);
26056 this.setPosition(this.gMapContext.location);
26058 this.fireEvent('initial', this, this.gMapContext.location);
26061 initOverlayView: function()
26065 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26069 _this.fireEvent('OverlayViewDraw', _this);
26074 _this.fireEvent('OverlayViewOnAdd', _this);
26077 onRemove: function()
26079 _this.fireEvent('OverlayViewOnRemove', _this);
26082 show: function(cpx)
26084 _this.fireEvent('OverlayViewShow', _this, cpx);
26089 _this.fireEvent('OverlayViewHide', _this);
26095 fromLatLngToContainerPixel: function(event)
26097 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26100 isApplied: function()
26102 return this.getGmapContext() == false ? false : true;
26105 getGmapContext: function()
26107 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26110 GMapContext: function()
26112 var position = new google.maps.LatLng(this.latitude, this.longitude);
26114 var _map = new google.maps.Map(this.el.dom, {
26117 mapTypeId: this.mapTypeId,
26118 mapTypeControl: this.mapTypeControl,
26119 disableDoubleClickZoom: this.disableDoubleClickZoom,
26120 scrollwheel: this.scrollwheel,
26121 streetViewControl: this.streetViewControl,
26122 locationName: this.locationName,
26123 draggable: this.draggable,
26124 enableAutocomplete: this.enableAutocomplete,
26125 enableReverseGeocode: this.enableReverseGeocode
26128 var _marker = new google.maps.Marker({
26129 position: position,
26131 title: this.markerTitle,
26132 draggable: this.draggable
26139 location: position,
26140 radius: this.radius,
26141 locationName: this.locationName,
26142 addressComponents: {
26143 formatted_address: null,
26144 addressLine1: null,
26145 addressLine2: null,
26147 streetNumber: null,
26151 stateOrProvince: null
26154 domContainer: this.el.dom,
26155 geodecoder: new google.maps.Geocoder()
26159 drawCircle: function(center, radius, options)
26161 if (this.gMapContext.circle != null) {
26162 this.gMapContext.circle.setMap(null);
26166 options = Roo.apply({}, options, {
26167 strokeColor: "#0000FF",
26168 strokeOpacity: .35,
26170 fillColor: "#0000FF",
26174 options.map = this.gMapContext.map;
26175 options.radius = radius;
26176 options.center = center;
26177 this.gMapContext.circle = new google.maps.Circle(options);
26178 return this.gMapContext.circle;
26184 setPosition: function(location)
26186 this.gMapContext.location = location;
26187 this.gMapContext.marker.setPosition(location);
26188 this.gMapContext.map.panTo(location);
26189 this.drawCircle(location, this.gMapContext.radius, {});
26193 if (this.gMapContext.settings.enableReverseGeocode) {
26194 this.gMapContext.geodecoder.geocode({
26195 latLng: this.gMapContext.location
26196 }, function(results, status) {
26198 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26199 _this.gMapContext.locationName = results[0].formatted_address;
26200 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26202 _this.fireEvent('positionchanged', this, location);
26209 this.fireEvent('positionchanged', this, location);
26214 google.maps.event.trigger(this.gMapContext.map, "resize");
26216 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26218 this.fireEvent('resize', this);
26221 setPositionByLatLng: function(latitude, longitude)
26223 this.setPosition(new google.maps.LatLng(latitude, longitude));
26226 getCurrentPosition: function()
26229 latitude: this.gMapContext.location.lat(),
26230 longitude: this.gMapContext.location.lng()
26234 getAddressName: function()
26236 return this.gMapContext.locationName;
26239 getAddressComponents: function()
26241 return this.gMapContext.addressComponents;
26244 address_component_from_google_geocode: function(address_components)
26248 for (var i = 0; i < address_components.length; i++) {
26249 var component = address_components[i];
26250 if (component.types.indexOf("postal_code") >= 0) {
26251 result.postalCode = component.short_name;
26252 } else if (component.types.indexOf("street_number") >= 0) {
26253 result.streetNumber = component.short_name;
26254 } else if (component.types.indexOf("route") >= 0) {
26255 result.streetName = component.short_name;
26256 } else if (component.types.indexOf("neighborhood") >= 0) {
26257 result.city = component.short_name;
26258 } else if (component.types.indexOf("locality") >= 0) {
26259 result.city = component.short_name;
26260 } else if (component.types.indexOf("sublocality") >= 0) {
26261 result.district = component.short_name;
26262 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26263 result.stateOrProvince = component.short_name;
26264 } else if (component.types.indexOf("country") >= 0) {
26265 result.country = component.short_name;
26269 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26270 result.addressLine2 = "";
26274 setZoomLevel: function(zoom)
26276 this.gMapContext.map.setZoom(zoom);
26289 this.fireEvent('show', this);
26300 this.fireEvent('hide', this);
26305 Roo.apply(Roo.bootstrap.LocationPicker, {
26307 OverlayView : function(map, options)
26309 options = options || {};
26323 * @class Roo.bootstrap.Alert
26324 * @extends Roo.bootstrap.Component
26325 * Bootstrap Alert class
26326 * @cfg {String} title The title of alert
26327 * @cfg {String} html The content of alert
26328 * @cfg {String} weight ( success | info | warning | danger )
26329 * @cfg {String} faicon font-awesomeicon
26332 * Create a new alert
26333 * @param {Object} config The config object
26337 Roo.bootstrap.Alert = function(config){
26338 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26342 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
26349 getAutoCreate : function()
26358 cls : 'roo-alert-icon'
26363 cls : 'roo-alert-title',
26368 cls : 'roo-alert-text',
26375 cfg.cn[0].cls += ' fa ' + this.faicon;
26379 cfg.cls += ' alert-' + this.weight;
26385 initEvents: function()
26387 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26390 setTitle : function(str)
26392 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26395 setText : function(str)
26397 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26400 setWeight : function(weight)
26403 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26406 this.weight = weight;
26408 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26411 setIcon : function(icon)
26414 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26417 this.faicon = icon;
26419 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26440 * @class Roo.bootstrap.UploadCropbox
26441 * @extends Roo.bootstrap.Component
26442 * Bootstrap UploadCropbox class
26443 * @cfg {String} emptyText show when image has been loaded
26444 * @cfg {String} rotateNotify show when image too small to rotate
26445 * @cfg {Number} errorTimeout default 3000
26446 * @cfg {Number} minWidth default 300
26447 * @cfg {Number} minHeight default 300
26448 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26449 * @cfg {Boolean} isDocument (true|false) default false
26450 * @cfg {String} url action url
26451 * @cfg {String} paramName default 'imageUpload'
26452 * @cfg {String} method default POST
26453 * @cfg {Boolean} loadMask (true|false) default true
26454 * @cfg {Boolean} loadingText default 'Loading...'
26457 * Create a new UploadCropbox
26458 * @param {Object} config The config object
26461 Roo.bootstrap.UploadCropbox = function(config){
26462 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26466 * @event beforeselectfile
26467 * Fire before select file
26468 * @param {Roo.bootstrap.UploadCropbox} this
26470 "beforeselectfile" : true,
26473 * Fire after initEvent
26474 * @param {Roo.bootstrap.UploadCropbox} this
26479 * Fire after initEvent
26480 * @param {Roo.bootstrap.UploadCropbox} this
26481 * @param {String} data
26486 * Fire when preparing the file data
26487 * @param {Roo.bootstrap.UploadCropbox} this
26488 * @param {Object} file
26493 * Fire when get exception
26494 * @param {Roo.bootstrap.UploadCropbox} this
26495 * @param {XMLHttpRequest} xhr
26497 "exception" : true,
26499 * @event beforeloadcanvas
26500 * Fire before load the canvas
26501 * @param {Roo.bootstrap.UploadCropbox} this
26502 * @param {String} src
26504 "beforeloadcanvas" : true,
26507 * Fire when trash image
26508 * @param {Roo.bootstrap.UploadCropbox} this
26513 * Fire when download the image
26514 * @param {Roo.bootstrap.UploadCropbox} this
26518 * @event footerbuttonclick
26519 * Fire when footerbuttonclick
26520 * @param {Roo.bootstrap.UploadCropbox} this
26521 * @param {String} type
26523 "footerbuttonclick" : true,
26527 * @param {Roo.bootstrap.UploadCropbox} this
26532 * Fire when rotate the image
26533 * @param {Roo.bootstrap.UploadCropbox} this
26534 * @param {String} pos
26539 * Fire when inspect the file
26540 * @param {Roo.bootstrap.UploadCropbox} this
26541 * @param {Object} file
26546 * Fire when xhr upload the file
26547 * @param {Roo.bootstrap.UploadCropbox} this
26548 * @param {Object} data
26553 * Fire when arrange the file data
26554 * @param {Roo.bootstrap.UploadCropbox} this
26555 * @param {Object} formData
26560 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26563 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
26565 emptyText : 'Click to upload image',
26566 rotateNotify : 'Image is too small to rotate',
26567 errorTimeout : 3000,
26581 cropType : 'image/jpeg',
26583 canvasLoaded : false,
26584 isDocument : false,
26586 paramName : 'imageUpload',
26588 loadingText : 'Loading...',
26591 getAutoCreate : function()
26595 cls : 'roo-upload-cropbox',
26599 cls : 'roo-upload-cropbox-selector',
26604 cls : 'roo-upload-cropbox-body',
26605 style : 'cursor:pointer',
26609 cls : 'roo-upload-cropbox-preview'
26613 cls : 'roo-upload-cropbox-thumb'
26617 cls : 'roo-upload-cropbox-empty-notify',
26618 html : this.emptyText
26622 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
26623 html : this.rotateNotify
26629 cls : 'roo-upload-cropbox-footer',
26632 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
26642 onRender : function(ct, position)
26644 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
26646 if (this.buttons.length) {
26648 Roo.each(this.buttons, function(bb) {
26650 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
26652 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
26658 this.maskEl = this.el;
26662 initEvents : function()
26664 this.urlAPI = (window.createObjectURL && window) ||
26665 (window.URL && URL.revokeObjectURL && URL) ||
26666 (window.webkitURL && webkitURL);
26668 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
26669 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26671 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
26672 this.selectorEl.hide();
26674 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
26675 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26677 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
26678 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26679 this.thumbEl.hide();
26681 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
26682 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26684 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
26685 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26686 this.errorEl.hide();
26688 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
26689 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26690 this.footerEl.hide();
26692 this.setThumbBoxSize();
26698 this.fireEvent('initial', this);
26705 window.addEventListener("resize", function() { _this.resize(); } );
26707 this.bodyEl.on('click', this.beforeSelectFile, this);
26710 this.bodyEl.on('touchstart', this.onTouchStart, this);
26711 this.bodyEl.on('touchmove', this.onTouchMove, this);
26712 this.bodyEl.on('touchend', this.onTouchEnd, this);
26716 this.bodyEl.on('mousedown', this.onMouseDown, this);
26717 this.bodyEl.on('mousemove', this.onMouseMove, this);
26718 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
26719 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
26720 Roo.get(document).on('mouseup', this.onMouseUp, this);
26723 this.selectorEl.on('change', this.onFileSelected, this);
26729 this.baseScale = 1;
26731 this.baseRotate = 1;
26732 this.dragable = false;
26733 this.pinching = false;
26736 this.cropData = false;
26737 this.notifyEl.dom.innerHTML = this.emptyText;
26739 this.selectorEl.dom.value = '';
26743 resize : function()
26745 if(this.fireEvent('resize', this) != false){
26746 this.setThumbBoxPosition();
26747 this.setCanvasPosition();
26751 onFooterButtonClick : function(e, el, o, type)
26754 case 'rotate-left' :
26755 this.onRotateLeft(e);
26757 case 'rotate-right' :
26758 this.onRotateRight(e);
26761 this.beforeSelectFile(e);
26776 this.fireEvent('footerbuttonclick', this, type);
26779 beforeSelectFile : function(e)
26781 e.preventDefault();
26783 if(this.fireEvent('beforeselectfile', this) != false){
26784 this.selectorEl.dom.click();
26788 onFileSelected : function(e)
26790 e.preventDefault();
26792 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26796 var file = this.selectorEl.dom.files[0];
26798 if(this.fireEvent('inspect', this, file) != false){
26799 this.prepare(file);
26804 trash : function(e)
26806 this.fireEvent('trash', this);
26809 download : function(e)
26811 this.fireEvent('download', this);
26814 loadCanvas : function(src)
26816 if(this.fireEvent('beforeloadcanvas', this, src) != false){
26820 this.imageEl = document.createElement('img');
26824 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
26826 this.imageEl.src = src;
26830 onLoadCanvas : function()
26832 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
26833 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
26835 this.bodyEl.un('click', this.beforeSelectFile, this);
26837 this.notifyEl.hide();
26838 this.thumbEl.show();
26839 this.footerEl.show();
26841 this.baseRotateLevel();
26843 if(this.isDocument){
26844 this.setThumbBoxSize();
26847 this.setThumbBoxPosition();
26849 this.baseScaleLevel();
26855 this.canvasLoaded = true;
26858 this.maskEl.unmask();
26863 setCanvasPosition : function()
26865 if(!this.canvasEl){
26869 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
26870 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
26872 this.previewEl.setLeft(pw);
26873 this.previewEl.setTop(ph);
26877 onMouseDown : function(e)
26881 this.dragable = true;
26882 this.pinching = false;
26884 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
26885 this.dragable = false;
26889 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26890 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26894 onMouseMove : function(e)
26898 if(!this.canvasLoaded){
26902 if (!this.dragable){
26906 var minX = Math.ceil(this.thumbEl.getLeft(true));
26907 var minY = Math.ceil(this.thumbEl.getTop(true));
26909 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
26910 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
26912 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26913 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26915 x = x - this.mouseX;
26916 y = y - this.mouseY;
26918 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
26919 var bgY = Math.ceil(y + this.previewEl.getTop(true));
26921 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
26922 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
26924 this.previewEl.setLeft(bgX);
26925 this.previewEl.setTop(bgY);
26927 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26928 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26931 onMouseUp : function(e)
26935 this.dragable = false;
26938 onMouseWheel : function(e)
26942 this.startScale = this.scale;
26944 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
26946 if(!this.zoomable()){
26947 this.scale = this.startScale;
26956 zoomable : function()
26958 var minScale = this.thumbEl.getWidth() / this.minWidth;
26960 if(this.minWidth < this.minHeight){
26961 minScale = this.thumbEl.getHeight() / this.minHeight;
26964 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
26965 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
26969 (this.rotate == 0 || this.rotate == 180) &&
26971 width > this.imageEl.OriginWidth ||
26972 height > this.imageEl.OriginHeight ||
26973 (width < this.minWidth && height < this.minHeight)
26981 (this.rotate == 90 || this.rotate == 270) &&
26983 width > this.imageEl.OriginWidth ||
26984 height > this.imageEl.OriginHeight ||
26985 (width < this.minHeight && height < this.minWidth)
26992 !this.isDocument &&
26993 (this.rotate == 0 || this.rotate == 180) &&
26995 width < this.minWidth ||
26996 width > this.imageEl.OriginWidth ||
26997 height < this.minHeight ||
26998 height > this.imageEl.OriginHeight
27005 !this.isDocument &&
27006 (this.rotate == 90 || this.rotate == 270) &&
27008 width < this.minHeight ||
27009 width > this.imageEl.OriginWidth ||
27010 height < this.minWidth ||
27011 height > this.imageEl.OriginHeight
27021 onRotateLeft : function(e)
27023 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27025 var minScale = this.thumbEl.getWidth() / this.minWidth;
27027 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27028 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27030 this.startScale = this.scale;
27032 while (this.getScaleLevel() < minScale){
27034 this.scale = this.scale + 1;
27036 if(!this.zoomable()){
27041 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27042 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27047 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27054 this.scale = this.startScale;
27056 this.onRotateFail();
27061 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27063 if(this.isDocument){
27064 this.setThumbBoxSize();
27065 this.setThumbBoxPosition();
27066 this.setCanvasPosition();
27071 this.fireEvent('rotate', this, 'left');
27075 onRotateRight : function(e)
27077 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27079 var minScale = this.thumbEl.getWidth() / this.minWidth;
27081 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27082 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27084 this.startScale = this.scale;
27086 while (this.getScaleLevel() < minScale){
27088 this.scale = this.scale + 1;
27090 if(!this.zoomable()){
27095 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27096 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27101 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27108 this.scale = this.startScale;
27110 this.onRotateFail();
27115 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27117 if(this.isDocument){
27118 this.setThumbBoxSize();
27119 this.setThumbBoxPosition();
27120 this.setCanvasPosition();
27125 this.fireEvent('rotate', this, 'right');
27128 onRotateFail : function()
27130 this.errorEl.show(true);
27134 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27139 this.previewEl.dom.innerHTML = '';
27141 var canvasEl = document.createElement("canvas");
27143 var contextEl = canvasEl.getContext("2d");
27145 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27146 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27147 var center = this.imageEl.OriginWidth / 2;
27149 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27150 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27151 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27152 center = this.imageEl.OriginHeight / 2;
27155 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27157 contextEl.translate(center, center);
27158 contextEl.rotate(this.rotate * Math.PI / 180);
27160 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27162 this.canvasEl = document.createElement("canvas");
27164 this.contextEl = this.canvasEl.getContext("2d");
27166 switch (this.rotate) {
27169 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27170 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27172 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27177 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27178 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27180 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27181 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);
27185 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27190 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27191 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27193 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27194 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);
27198 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);
27203 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27204 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27206 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27207 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27211 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);
27218 this.previewEl.appendChild(this.canvasEl);
27220 this.setCanvasPosition();
27225 if(!this.canvasLoaded){
27229 var imageCanvas = document.createElement("canvas");
27231 var imageContext = imageCanvas.getContext("2d");
27233 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27234 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27236 var center = imageCanvas.width / 2;
27238 imageContext.translate(center, center);
27240 imageContext.rotate(this.rotate * Math.PI / 180);
27242 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27244 var canvas = document.createElement("canvas");
27246 var context = canvas.getContext("2d");
27248 canvas.width = this.minWidth;
27249 canvas.height = this.minHeight;
27251 switch (this.rotate) {
27254 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27255 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27257 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27258 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27260 var targetWidth = this.minWidth - 2 * x;
27261 var targetHeight = this.minHeight - 2 * y;
27265 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27266 scale = targetWidth / width;
27269 if(x > 0 && y == 0){
27270 scale = targetHeight / height;
27273 if(x > 0 && y > 0){
27274 scale = targetWidth / width;
27276 if(width < height){
27277 scale = targetHeight / height;
27281 context.scale(scale, scale);
27283 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27284 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27286 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27287 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27289 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27294 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27295 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27297 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27298 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27300 var targetWidth = this.minWidth - 2 * x;
27301 var targetHeight = this.minHeight - 2 * y;
27305 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27306 scale = targetWidth / width;
27309 if(x > 0 && y == 0){
27310 scale = targetHeight / height;
27313 if(x > 0 && y > 0){
27314 scale = targetWidth / width;
27316 if(width < height){
27317 scale = targetHeight / height;
27321 context.scale(scale, scale);
27323 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27324 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27326 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27327 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27329 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27331 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27336 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27337 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27339 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27340 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27342 var targetWidth = this.minWidth - 2 * x;
27343 var targetHeight = this.minHeight - 2 * y;
27347 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27348 scale = targetWidth / width;
27351 if(x > 0 && y == 0){
27352 scale = targetHeight / height;
27355 if(x > 0 && y > 0){
27356 scale = targetWidth / width;
27358 if(width < height){
27359 scale = targetHeight / height;
27363 context.scale(scale, scale);
27365 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27366 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27368 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27369 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27371 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27372 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27374 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27379 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27380 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27382 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27383 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27385 var targetWidth = this.minWidth - 2 * x;
27386 var targetHeight = this.minHeight - 2 * y;
27390 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27391 scale = targetWidth / width;
27394 if(x > 0 && y == 0){
27395 scale = targetHeight / height;
27398 if(x > 0 && y > 0){
27399 scale = targetWidth / width;
27401 if(width < height){
27402 scale = targetHeight / height;
27406 context.scale(scale, scale);
27408 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27409 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27411 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27412 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27414 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27416 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27423 this.cropData = canvas.toDataURL(this.cropType);
27425 if(this.fireEvent('crop', this, this.cropData) !== false){
27426 this.process(this.file, this.cropData);
27433 setThumbBoxSize : function()
27437 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27438 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27439 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27441 this.minWidth = width;
27442 this.minHeight = height;
27444 if(this.rotate == 90 || this.rotate == 270){
27445 this.minWidth = height;
27446 this.minHeight = width;
27451 width = Math.ceil(this.minWidth * height / this.minHeight);
27453 if(this.minWidth > this.minHeight){
27455 height = Math.ceil(this.minHeight * width / this.minWidth);
27458 this.thumbEl.setStyle({
27459 width : width + 'px',
27460 height : height + 'px'
27467 setThumbBoxPosition : function()
27469 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27470 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27472 this.thumbEl.setLeft(x);
27473 this.thumbEl.setTop(y);
27477 baseRotateLevel : function()
27479 this.baseRotate = 1;
27482 typeof(this.exif) != 'undefined' &&
27483 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27484 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27486 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27489 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27493 baseScaleLevel : function()
27497 if(this.isDocument){
27499 if(this.baseRotate == 6 || this.baseRotate == 8){
27501 height = this.thumbEl.getHeight();
27502 this.baseScale = height / this.imageEl.OriginWidth;
27504 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27505 width = this.thumbEl.getWidth();
27506 this.baseScale = width / this.imageEl.OriginHeight;
27512 height = this.thumbEl.getHeight();
27513 this.baseScale = height / this.imageEl.OriginHeight;
27515 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27516 width = this.thumbEl.getWidth();
27517 this.baseScale = width / this.imageEl.OriginWidth;
27523 if(this.baseRotate == 6 || this.baseRotate == 8){
27525 width = this.thumbEl.getHeight();
27526 this.baseScale = width / this.imageEl.OriginHeight;
27528 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27529 height = this.thumbEl.getWidth();
27530 this.baseScale = height / this.imageEl.OriginHeight;
27533 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27534 height = this.thumbEl.getWidth();
27535 this.baseScale = height / this.imageEl.OriginHeight;
27537 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27538 width = this.thumbEl.getHeight();
27539 this.baseScale = width / this.imageEl.OriginWidth;
27546 width = this.thumbEl.getWidth();
27547 this.baseScale = width / this.imageEl.OriginWidth;
27549 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27550 height = this.thumbEl.getHeight();
27551 this.baseScale = height / this.imageEl.OriginHeight;
27554 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27556 height = this.thumbEl.getHeight();
27557 this.baseScale = height / this.imageEl.OriginHeight;
27559 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27560 width = this.thumbEl.getWidth();
27561 this.baseScale = width / this.imageEl.OriginWidth;
27569 getScaleLevel : function()
27571 return this.baseScale * Math.pow(1.1, this.scale);
27574 onTouchStart : function(e)
27576 if(!this.canvasLoaded){
27577 this.beforeSelectFile(e);
27581 var touches = e.browserEvent.touches;
27587 if(touches.length == 1){
27588 this.onMouseDown(e);
27592 if(touches.length != 2){
27598 for(var i = 0, finger; finger = touches[i]; i++){
27599 coords.push(finger.pageX, finger.pageY);
27602 var x = Math.pow(coords[0] - coords[2], 2);
27603 var y = Math.pow(coords[1] - coords[3], 2);
27605 this.startDistance = Math.sqrt(x + y);
27607 this.startScale = this.scale;
27609 this.pinching = true;
27610 this.dragable = false;
27614 onTouchMove : function(e)
27616 if(!this.pinching && !this.dragable){
27620 var touches = e.browserEvent.touches;
27627 this.onMouseMove(e);
27633 for(var i = 0, finger; finger = touches[i]; i++){
27634 coords.push(finger.pageX, finger.pageY);
27637 var x = Math.pow(coords[0] - coords[2], 2);
27638 var y = Math.pow(coords[1] - coords[3], 2);
27640 this.endDistance = Math.sqrt(x + y);
27642 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
27644 if(!this.zoomable()){
27645 this.scale = this.startScale;
27653 onTouchEnd : function(e)
27655 this.pinching = false;
27656 this.dragable = false;
27660 process : function(file, crop)
27663 this.maskEl.mask(this.loadingText);
27666 this.xhr = new XMLHttpRequest();
27668 file.xhr = this.xhr;
27670 this.xhr.open(this.method, this.url, true);
27673 "Accept": "application/json",
27674 "Cache-Control": "no-cache",
27675 "X-Requested-With": "XMLHttpRequest"
27678 for (var headerName in headers) {
27679 var headerValue = headers[headerName];
27681 this.xhr.setRequestHeader(headerName, headerValue);
27687 this.xhr.onload = function()
27689 _this.xhrOnLoad(_this.xhr);
27692 this.xhr.onerror = function()
27694 _this.xhrOnError(_this.xhr);
27697 var formData = new FormData();
27699 formData.append('returnHTML', 'NO');
27702 formData.append('crop', crop);
27705 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
27706 formData.append(this.paramName, file, file.name);
27709 if(typeof(file.filename) != 'undefined'){
27710 formData.append('filename', file.filename);
27713 if(typeof(file.mimetype) != 'undefined'){
27714 formData.append('mimetype', file.mimetype);
27717 if(this.fireEvent('arrange', this, formData) != false){
27718 this.xhr.send(formData);
27722 xhrOnLoad : function(xhr)
27725 this.maskEl.unmask();
27728 if (xhr.readyState !== 4) {
27729 this.fireEvent('exception', this, xhr);
27733 var response = Roo.decode(xhr.responseText);
27735 if(!response.success){
27736 this.fireEvent('exception', this, xhr);
27740 var response = Roo.decode(xhr.responseText);
27742 this.fireEvent('upload', this, response);
27746 xhrOnError : function()
27749 this.maskEl.unmask();
27752 Roo.log('xhr on error');
27754 var response = Roo.decode(xhr.responseText);
27760 prepare : function(file)
27763 this.maskEl.mask(this.loadingText);
27769 if(typeof(file) === 'string'){
27770 this.loadCanvas(file);
27774 if(!file || !this.urlAPI){
27779 this.cropType = file.type;
27783 if(this.fireEvent('prepare', this, this.file) != false){
27785 var reader = new FileReader();
27787 reader.onload = function (e) {
27788 if (e.target.error) {
27789 Roo.log(e.target.error);
27793 var buffer = e.target.result,
27794 dataView = new DataView(buffer),
27796 maxOffset = dataView.byteLength - 4,
27800 if (dataView.getUint16(0) === 0xffd8) {
27801 while (offset < maxOffset) {
27802 markerBytes = dataView.getUint16(offset);
27804 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
27805 markerLength = dataView.getUint16(offset + 2) + 2;
27806 if (offset + markerLength > dataView.byteLength) {
27807 Roo.log('Invalid meta data: Invalid segment size.');
27811 if(markerBytes == 0xffe1){
27812 _this.parseExifData(
27819 offset += markerLength;
27829 var url = _this.urlAPI.createObjectURL(_this.file);
27831 _this.loadCanvas(url);
27836 reader.readAsArrayBuffer(this.file);
27842 parseExifData : function(dataView, offset, length)
27844 var tiffOffset = offset + 10,
27848 if (dataView.getUint32(offset + 4) !== 0x45786966) {
27849 // No Exif data, might be XMP data instead
27853 // Check for the ASCII code for "Exif" (0x45786966):
27854 if (dataView.getUint32(offset + 4) !== 0x45786966) {
27855 // No Exif data, might be XMP data instead
27858 if (tiffOffset + 8 > dataView.byteLength) {
27859 Roo.log('Invalid Exif data: Invalid segment size.');
27862 // Check for the two null bytes:
27863 if (dataView.getUint16(offset + 8) !== 0x0000) {
27864 Roo.log('Invalid Exif data: Missing byte alignment offset.');
27867 // Check the byte alignment:
27868 switch (dataView.getUint16(tiffOffset)) {
27870 littleEndian = true;
27873 littleEndian = false;
27876 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
27879 // Check for the TIFF tag marker (0x002A):
27880 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
27881 Roo.log('Invalid Exif data: Missing TIFF marker.');
27884 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
27885 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
27887 this.parseExifTags(
27890 tiffOffset + dirOffset,
27895 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
27900 if (dirOffset + 6 > dataView.byteLength) {
27901 Roo.log('Invalid Exif data: Invalid directory offset.');
27904 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
27905 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
27906 if (dirEndOffset + 4 > dataView.byteLength) {
27907 Roo.log('Invalid Exif data: Invalid directory size.');
27910 for (i = 0; i < tagsNumber; i += 1) {
27914 dirOffset + 2 + 12 * i, // tag offset
27918 // Return the offset to the next directory:
27919 return dataView.getUint32(dirEndOffset, littleEndian);
27922 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
27924 var tag = dataView.getUint16(offset, littleEndian);
27926 this.exif[tag] = this.getExifValue(
27930 dataView.getUint16(offset + 2, littleEndian), // tag type
27931 dataView.getUint32(offset + 4, littleEndian), // tag length
27936 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
27938 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
27947 Roo.log('Invalid Exif data: Invalid tag type.');
27951 tagSize = tagType.size * length;
27952 // Determine if the value is contained in the dataOffset bytes,
27953 // or if the value at the dataOffset is a pointer to the actual data:
27954 dataOffset = tagSize > 4 ?
27955 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
27956 if (dataOffset + tagSize > dataView.byteLength) {
27957 Roo.log('Invalid Exif data: Invalid data offset.');
27960 if (length === 1) {
27961 return tagType.getValue(dataView, dataOffset, littleEndian);
27964 for (i = 0; i < length; i += 1) {
27965 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
27968 if (tagType.ascii) {
27970 // Concatenate the chars:
27971 for (i = 0; i < values.length; i += 1) {
27973 // Ignore the terminating NULL byte(s):
27974 if (c === '\u0000') {
27986 Roo.apply(Roo.bootstrap.UploadCropbox, {
27988 'Orientation': 0x0112
27992 1: 0, //'top-left',
27994 3: 180, //'bottom-right',
27995 // 4: 'bottom-left',
27997 6: 90, //'right-top',
27998 // 7: 'right-bottom',
27999 8: 270 //'left-bottom'
28003 // byte, 8-bit unsigned int:
28005 getValue: function (dataView, dataOffset) {
28006 return dataView.getUint8(dataOffset);
28010 // ascii, 8-bit byte:
28012 getValue: function (dataView, dataOffset) {
28013 return String.fromCharCode(dataView.getUint8(dataOffset));
28018 // short, 16 bit int:
28020 getValue: function (dataView, dataOffset, littleEndian) {
28021 return dataView.getUint16(dataOffset, littleEndian);
28025 // long, 32 bit int:
28027 getValue: function (dataView, dataOffset, littleEndian) {
28028 return dataView.getUint32(dataOffset, littleEndian);
28032 // rational = two long values, first is numerator, second is denominator:
28034 getValue: function (dataView, dataOffset, littleEndian) {
28035 return dataView.getUint32(dataOffset, littleEndian) /
28036 dataView.getUint32(dataOffset + 4, littleEndian);
28040 // slong, 32 bit signed int:
28042 getValue: function (dataView, dataOffset, littleEndian) {
28043 return dataView.getInt32(dataOffset, littleEndian);
28047 // srational, two slongs, first is numerator, second is denominator:
28049 getValue: function (dataView, dataOffset, littleEndian) {
28050 return dataView.getInt32(dataOffset, littleEndian) /
28051 dataView.getInt32(dataOffset + 4, littleEndian);
28061 cls : 'btn-group roo-upload-cropbox-rotate-left',
28062 action : 'rotate-left',
28066 cls : 'btn btn-default',
28067 html : '<i class="fa fa-undo"></i>'
28073 cls : 'btn-group roo-upload-cropbox-picture',
28074 action : 'picture',
28078 cls : 'btn btn-default',
28079 html : '<i class="fa fa-picture-o"></i>'
28085 cls : 'btn-group roo-upload-cropbox-rotate-right',
28086 action : 'rotate-right',
28090 cls : 'btn btn-default',
28091 html : '<i class="fa fa-repeat"></i>'
28099 cls : 'btn-group roo-upload-cropbox-rotate-left',
28100 action : 'rotate-left',
28104 cls : 'btn btn-default',
28105 html : '<i class="fa fa-undo"></i>'
28111 cls : 'btn-group roo-upload-cropbox-download',
28112 action : 'download',
28116 cls : 'btn btn-default',
28117 html : '<i class="fa fa-download"></i>'
28123 cls : 'btn-group roo-upload-cropbox-crop',
28128 cls : 'btn btn-default',
28129 html : '<i class="fa fa-crop"></i>'
28135 cls : 'btn-group roo-upload-cropbox-trash',
28140 cls : 'btn btn-default',
28141 html : '<i class="fa fa-trash"></i>'
28147 cls : 'btn-group roo-upload-cropbox-rotate-right',
28148 action : 'rotate-right',
28152 cls : 'btn btn-default',
28153 html : '<i class="fa fa-repeat"></i>'
28161 cls : 'btn-group roo-upload-cropbox-rotate-left',
28162 action : 'rotate-left',
28166 cls : 'btn btn-default',
28167 html : '<i class="fa fa-undo"></i>'
28173 cls : 'btn-group roo-upload-cropbox-rotate-right',
28174 action : 'rotate-right',
28178 cls : 'btn btn-default',
28179 html : '<i class="fa fa-repeat"></i>'
28192 * @class Roo.bootstrap.DocumentManager
28193 * @extends Roo.bootstrap.Component
28194 * Bootstrap DocumentManager class
28195 * @cfg {String} paramName default 'imageUpload'
28196 * @cfg {String} toolTipName default 'filename'
28197 * @cfg {String} method default POST
28198 * @cfg {String} url action url
28199 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28200 * @cfg {Boolean} multiple multiple upload default true
28201 * @cfg {Number} thumbSize default 300
28202 * @cfg {String} fieldLabel
28203 * @cfg {Number} labelWidth default 4
28204 * @cfg {String} labelAlign (left|top) default left
28205 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28206 * @cfg {Number} labellg set the width of label (1-12)
28207 * @cfg {Number} labelmd set the width of label (1-12)
28208 * @cfg {Number} labelsm set the width of label (1-12)
28209 * @cfg {Number} labelxs set the width of label (1-12)
28212 * Create a new DocumentManager
28213 * @param {Object} config The config object
28216 Roo.bootstrap.DocumentManager = function(config){
28217 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28220 this.delegates = [];
28225 * Fire when initial the DocumentManager
28226 * @param {Roo.bootstrap.DocumentManager} this
28231 * inspect selected file
28232 * @param {Roo.bootstrap.DocumentManager} this
28233 * @param {File} file
28238 * Fire when xhr load exception
28239 * @param {Roo.bootstrap.DocumentManager} this
28240 * @param {XMLHttpRequest} xhr
28242 "exception" : true,
28244 * @event afterupload
28245 * Fire when xhr load exception
28246 * @param {Roo.bootstrap.DocumentManager} this
28247 * @param {XMLHttpRequest} xhr
28249 "afterupload" : true,
28252 * prepare the form data
28253 * @param {Roo.bootstrap.DocumentManager} this
28254 * @param {Object} formData
28259 * Fire when remove the file
28260 * @param {Roo.bootstrap.DocumentManager} this
28261 * @param {Object} file
28266 * Fire after refresh the file
28267 * @param {Roo.bootstrap.DocumentManager} this
28272 * Fire after click the image
28273 * @param {Roo.bootstrap.DocumentManager} this
28274 * @param {Object} file
28279 * Fire when upload a image and editable set to true
28280 * @param {Roo.bootstrap.DocumentManager} this
28281 * @param {Object} file
28285 * @event beforeselectfile
28286 * Fire before select file
28287 * @param {Roo.bootstrap.DocumentManager} this
28289 "beforeselectfile" : true,
28292 * Fire before process file
28293 * @param {Roo.bootstrap.DocumentManager} this
28294 * @param {Object} file
28301 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
28310 paramName : 'imageUpload',
28311 toolTipName : 'filename',
28314 labelAlign : 'left',
28324 getAutoCreate : function()
28326 var managerWidget = {
28328 cls : 'roo-document-manager',
28332 cls : 'roo-document-manager-selector',
28337 cls : 'roo-document-manager-uploader',
28341 cls : 'roo-document-manager-upload-btn',
28342 html : '<i class="fa fa-plus"></i>'
28353 cls : 'column col-md-12',
28358 if(this.fieldLabel.length){
28363 cls : 'column col-md-12',
28364 html : this.fieldLabel
28368 cls : 'column col-md-12',
28373 if(this.labelAlign == 'left'){
28378 html : this.fieldLabel
28387 if(this.labelWidth > 12){
28388 content[0].style = "width: " + this.labelWidth + 'px';
28391 if(this.labelWidth < 13 && this.labelmd == 0){
28392 this.labelmd = this.labelWidth;
28395 if(this.labellg > 0){
28396 content[0].cls += ' col-lg-' + this.labellg;
28397 content[1].cls += ' col-lg-' + (12 - this.labellg);
28400 if(this.labelmd > 0){
28401 content[0].cls += ' col-md-' + this.labelmd;
28402 content[1].cls += ' col-md-' + (12 - this.labelmd);
28405 if(this.labelsm > 0){
28406 content[0].cls += ' col-sm-' + this.labelsm;
28407 content[1].cls += ' col-sm-' + (12 - this.labelsm);
28410 if(this.labelxs > 0){
28411 content[0].cls += ' col-xs-' + this.labelxs;
28412 content[1].cls += ' col-xs-' + (12 - this.labelxs);
28420 cls : 'row clearfix',
28428 initEvents : function()
28430 this.managerEl = this.el.select('.roo-document-manager', true).first();
28431 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28433 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28434 this.selectorEl.hide();
28437 this.selectorEl.attr('multiple', 'multiple');
28440 this.selectorEl.on('change', this.onFileSelected, this);
28442 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28443 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28445 this.uploader.on('click', this.onUploaderClick, this);
28447 this.renderProgressDialog();
28451 window.addEventListener("resize", function() { _this.refresh(); } );
28453 this.fireEvent('initial', this);
28456 renderProgressDialog : function()
28460 this.progressDialog = new Roo.bootstrap.Modal({
28461 cls : 'roo-document-manager-progress-dialog',
28462 allow_close : false,
28472 btnclick : function() {
28473 _this.uploadCancel();
28479 this.progressDialog.render(Roo.get(document.body));
28481 this.progress = new Roo.bootstrap.Progress({
28482 cls : 'roo-document-manager-progress',
28487 this.progress.render(this.progressDialog.getChildContainer());
28489 this.progressBar = new Roo.bootstrap.ProgressBar({
28490 cls : 'roo-document-manager-progress-bar',
28493 aria_valuemax : 12,
28497 this.progressBar.render(this.progress.getChildContainer());
28500 onUploaderClick : function(e)
28502 e.preventDefault();
28504 if(this.fireEvent('beforeselectfile', this) != false){
28505 this.selectorEl.dom.click();
28510 onFileSelected : function(e)
28512 e.preventDefault();
28514 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28518 Roo.each(this.selectorEl.dom.files, function(file){
28519 if(this.fireEvent('inspect', this, file) != false){
28520 this.files.push(file);
28530 this.selectorEl.dom.value = '';
28532 if(!this.files.length){
28536 if(this.boxes > 0 && this.files.length > this.boxes){
28537 this.files = this.files.slice(0, this.boxes);
28540 this.uploader.show();
28542 if(this.boxes > 0 && this.files.length > this.boxes - 1){
28543 this.uploader.hide();
28552 Roo.each(this.files, function(file){
28554 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28555 var f = this.renderPreview(file);
28560 if(file.type.indexOf('image') != -1){
28561 this.delegates.push(
28563 _this.process(file);
28564 }).createDelegate(this)
28572 _this.process(file);
28573 }).createDelegate(this)
28578 this.files = files;
28580 this.delegates = this.delegates.concat(docs);
28582 if(!this.delegates.length){
28587 this.progressBar.aria_valuemax = this.delegates.length;
28594 arrange : function()
28596 if(!this.delegates.length){
28597 this.progressDialog.hide();
28602 var delegate = this.delegates.shift();
28604 this.progressDialog.show();
28606 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
28608 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
28613 refresh : function()
28615 this.uploader.show();
28617 if(this.boxes > 0 && this.files.length > this.boxes - 1){
28618 this.uploader.hide();
28621 Roo.isTouch ? this.closable(false) : this.closable(true);
28623 this.fireEvent('refresh', this);
28626 onRemove : function(e, el, o)
28628 e.preventDefault();
28630 this.fireEvent('remove', this, o);
28634 remove : function(o)
28638 Roo.each(this.files, function(file){
28639 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
28648 this.files = files;
28655 Roo.each(this.files, function(file){
28660 file.target.remove();
28669 onClick : function(e, el, o)
28671 e.preventDefault();
28673 this.fireEvent('click', this, o);
28677 closable : function(closable)
28679 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
28681 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28693 xhrOnLoad : function(xhr)
28695 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28699 if (xhr.readyState !== 4) {
28701 this.fireEvent('exception', this, xhr);
28705 var response = Roo.decode(xhr.responseText);
28707 if(!response.success){
28709 this.fireEvent('exception', this, xhr);
28713 var file = this.renderPreview(response.data);
28715 this.files.push(file);
28719 this.fireEvent('afterupload', this, xhr);
28723 xhrOnError : function(xhr)
28725 Roo.log('xhr on error');
28727 var response = Roo.decode(xhr.responseText);
28734 process : function(file)
28736 if(this.fireEvent('process', this, file) !== false){
28737 if(this.editable && file.type.indexOf('image') != -1){
28738 this.fireEvent('edit', this, file);
28742 this.uploadStart(file, false);
28749 uploadStart : function(file, crop)
28751 this.xhr = new XMLHttpRequest();
28753 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28758 file.xhr = this.xhr;
28760 this.managerEl.createChild({
28762 cls : 'roo-document-manager-loading',
28766 tooltip : file.name,
28767 cls : 'roo-document-manager-thumb',
28768 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28774 this.xhr.open(this.method, this.url, true);
28777 "Accept": "application/json",
28778 "Cache-Control": "no-cache",
28779 "X-Requested-With": "XMLHttpRequest"
28782 for (var headerName in headers) {
28783 var headerValue = headers[headerName];
28785 this.xhr.setRequestHeader(headerName, headerValue);
28791 this.xhr.onload = function()
28793 _this.xhrOnLoad(_this.xhr);
28796 this.xhr.onerror = function()
28798 _this.xhrOnError(_this.xhr);
28801 var formData = new FormData();
28803 formData.append('returnHTML', 'NO');
28806 formData.append('crop', crop);
28809 formData.append(this.paramName, file, file.name);
28816 if(this.fireEvent('prepare', this, formData, options) != false){
28818 if(options.manually){
28822 this.xhr.send(formData);
28826 this.uploadCancel();
28829 uploadCancel : function()
28835 this.delegates = [];
28837 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28844 renderPreview : function(file)
28846 if(typeof(file.target) != 'undefined' && file.target){
28850 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
28852 var previewEl = this.managerEl.createChild({
28854 cls : 'roo-document-manager-preview',
28858 tooltip : file[this.toolTipName],
28859 cls : 'roo-document-manager-thumb',
28860 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
28865 html : '<i class="fa fa-times-circle"></i>'
28870 var close = previewEl.select('button.close', true).first();
28872 close.on('click', this.onRemove, this, file);
28874 file.target = previewEl;
28876 var image = previewEl.select('img', true).first();
28880 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
28882 image.on('click', this.onClick, this, file);
28888 onPreviewLoad : function(file, image)
28890 if(typeof(file.target) == 'undefined' || !file.target){
28894 var width = image.dom.naturalWidth || image.dom.width;
28895 var height = image.dom.naturalHeight || image.dom.height;
28897 if(width > height){
28898 file.target.addClass('wide');
28902 file.target.addClass('tall');
28907 uploadFromSource : function(file, crop)
28909 this.xhr = new XMLHttpRequest();
28911 this.managerEl.createChild({
28913 cls : 'roo-document-manager-loading',
28917 tooltip : file.name,
28918 cls : 'roo-document-manager-thumb',
28919 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28925 this.xhr.open(this.method, this.url, true);
28928 "Accept": "application/json",
28929 "Cache-Control": "no-cache",
28930 "X-Requested-With": "XMLHttpRequest"
28933 for (var headerName in headers) {
28934 var headerValue = headers[headerName];
28936 this.xhr.setRequestHeader(headerName, headerValue);
28942 this.xhr.onload = function()
28944 _this.xhrOnLoad(_this.xhr);
28947 this.xhr.onerror = function()
28949 _this.xhrOnError(_this.xhr);
28952 var formData = new FormData();
28954 formData.append('returnHTML', 'NO');
28956 formData.append('crop', crop);
28958 if(typeof(file.filename) != 'undefined'){
28959 formData.append('filename', file.filename);
28962 if(typeof(file.mimetype) != 'undefined'){
28963 formData.append('mimetype', file.mimetype);
28968 if(this.fireEvent('prepare', this, formData) != false){
28969 this.xhr.send(formData);
28979 * @class Roo.bootstrap.DocumentViewer
28980 * @extends Roo.bootstrap.Component
28981 * Bootstrap DocumentViewer class
28982 * @cfg {Boolean} showDownload (true|false) show download button (default true)
28983 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
28986 * Create a new DocumentViewer
28987 * @param {Object} config The config object
28990 Roo.bootstrap.DocumentViewer = function(config){
28991 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
28996 * Fire after initEvent
28997 * @param {Roo.bootstrap.DocumentViewer} this
29003 * @param {Roo.bootstrap.DocumentViewer} this
29008 * Fire after download button
29009 * @param {Roo.bootstrap.DocumentViewer} this
29014 * Fire after trash button
29015 * @param {Roo.bootstrap.DocumentViewer} this
29022 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
29024 showDownload : true,
29028 getAutoCreate : function()
29032 cls : 'roo-document-viewer',
29036 cls : 'roo-document-viewer-body',
29040 cls : 'roo-document-viewer-thumb',
29044 cls : 'roo-document-viewer-image'
29052 cls : 'roo-document-viewer-footer',
29055 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29059 cls : 'btn-group roo-document-viewer-download',
29063 cls : 'btn btn-default',
29064 html : '<i class="fa fa-download"></i>'
29070 cls : 'btn-group roo-document-viewer-trash',
29074 cls : 'btn btn-default',
29075 html : '<i class="fa fa-trash"></i>'
29088 initEvents : function()
29090 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29091 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29093 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29094 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29096 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29097 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29099 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29100 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29102 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29103 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29105 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29106 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29108 this.bodyEl.on('click', this.onClick, this);
29109 this.downloadBtn.on('click', this.onDownload, this);
29110 this.trashBtn.on('click', this.onTrash, this);
29112 this.downloadBtn.hide();
29113 this.trashBtn.hide();
29115 if(this.showDownload){
29116 this.downloadBtn.show();
29119 if(this.showTrash){
29120 this.trashBtn.show();
29123 if(!this.showDownload && !this.showTrash) {
29124 this.footerEl.hide();
29129 initial : function()
29131 this.fireEvent('initial', this);
29135 onClick : function(e)
29137 e.preventDefault();
29139 this.fireEvent('click', this);
29142 onDownload : function(e)
29144 e.preventDefault();
29146 this.fireEvent('download', this);
29149 onTrash : function(e)
29151 e.preventDefault();
29153 this.fireEvent('trash', this);
29165 * @class Roo.bootstrap.NavProgressBar
29166 * @extends Roo.bootstrap.Component
29167 * Bootstrap NavProgressBar class
29170 * Create a new nav progress bar
29171 * @param {Object} config The config object
29174 Roo.bootstrap.NavProgressBar = function(config){
29175 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29177 this.bullets = this.bullets || [];
29179 // Roo.bootstrap.NavProgressBar.register(this);
29183 * Fires when the active item changes
29184 * @param {Roo.bootstrap.NavProgressBar} this
29185 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29186 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
29193 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
29198 getAutoCreate : function()
29200 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29204 cls : 'roo-navigation-bar-group',
29208 cls : 'roo-navigation-top-bar'
29212 cls : 'roo-navigation-bullets-bar',
29216 cls : 'roo-navigation-bar'
29223 cls : 'roo-navigation-bottom-bar'
29233 initEvents: function()
29238 onRender : function(ct, position)
29240 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29242 if(this.bullets.length){
29243 Roo.each(this.bullets, function(b){
29252 addItem : function(cfg)
29254 var item = new Roo.bootstrap.NavProgressItem(cfg);
29256 item.parentId = this.id;
29257 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29260 var top = new Roo.bootstrap.Element({
29262 cls : 'roo-navigation-bar-text'
29265 var bottom = new Roo.bootstrap.Element({
29267 cls : 'roo-navigation-bar-text'
29270 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29271 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29273 var topText = new Roo.bootstrap.Element({
29275 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29278 var bottomText = new Roo.bootstrap.Element({
29280 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29283 topText.onRender(top.el, null);
29284 bottomText.onRender(bottom.el, null);
29287 item.bottomEl = bottom;
29290 this.barItems.push(item);
29295 getActive : function()
29297 var active = false;
29299 Roo.each(this.barItems, function(v){
29301 if (!v.isActive()) {
29313 setActiveItem : function(item)
29317 Roo.each(this.barItems, function(v){
29318 if (v.rid == item.rid) {
29322 if (v.isActive()) {
29323 v.setActive(false);
29328 item.setActive(true);
29330 this.fireEvent('changed', this, item, prev);
29333 getBarItem: function(rid)
29337 Roo.each(this.barItems, function(e) {
29338 if (e.rid != rid) {
29349 indexOfItem : function(item)
29353 Roo.each(this.barItems, function(v, i){
29355 if (v.rid != item.rid) {
29366 setActiveNext : function()
29368 var i = this.indexOfItem(this.getActive());
29370 if (i > this.barItems.length) {
29374 this.setActiveItem(this.barItems[i+1]);
29377 setActivePrev : function()
29379 var i = this.indexOfItem(this.getActive());
29385 this.setActiveItem(this.barItems[i-1]);
29388 format : function()
29390 if(!this.barItems.length){
29394 var width = 100 / this.barItems.length;
29396 Roo.each(this.barItems, function(i){
29397 i.el.setStyle('width', width + '%');
29398 i.topEl.el.setStyle('width', width + '%');
29399 i.bottomEl.el.setStyle('width', width + '%');
29408 * Nav Progress Item
29413 * @class Roo.bootstrap.NavProgressItem
29414 * @extends Roo.bootstrap.Component
29415 * Bootstrap NavProgressItem class
29416 * @cfg {String} rid the reference id
29417 * @cfg {Boolean} active (true|false) Is item active default false
29418 * @cfg {Boolean} disabled (true|false) Is item active default false
29419 * @cfg {String} html
29420 * @cfg {String} position (top|bottom) text position default bottom
29421 * @cfg {String} icon show icon instead of number
29424 * Create a new NavProgressItem
29425 * @param {Object} config The config object
29427 Roo.bootstrap.NavProgressItem = function(config){
29428 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29433 * The raw click event for the entire grid.
29434 * @param {Roo.bootstrap.NavProgressItem} this
29435 * @param {Roo.EventObject} e
29442 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
29448 position : 'bottom',
29451 getAutoCreate : function()
29453 var iconCls = 'roo-navigation-bar-item-icon';
29455 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29459 cls: 'roo-navigation-bar-item',
29469 cfg.cls += ' active';
29472 cfg.cls += ' disabled';
29478 disable : function()
29480 this.setDisabled(true);
29483 enable : function()
29485 this.setDisabled(false);
29488 initEvents: function()
29490 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29492 this.iconEl.on('click', this.onClick, this);
29495 onClick : function(e)
29497 e.preventDefault();
29503 if(this.fireEvent('click', this, e) === false){
29507 this.parent().setActiveItem(this);
29510 isActive: function ()
29512 return this.active;
29515 setActive : function(state)
29517 if(this.active == state){
29521 this.active = state;
29524 this.el.addClass('active');
29528 this.el.removeClass('active');
29533 setDisabled : function(state)
29535 if(this.disabled == state){
29539 this.disabled = state;
29542 this.el.addClass('disabled');
29546 this.el.removeClass('disabled');
29549 tooltipEl : function()
29551 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29564 * @class Roo.bootstrap.FieldLabel
29565 * @extends Roo.bootstrap.Component
29566 * Bootstrap FieldLabel class
29567 * @cfg {String} html contents of the element
29568 * @cfg {String} tag tag of the element default label
29569 * @cfg {String} cls class of the element
29570 * @cfg {String} target label target
29571 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
29572 * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
29573 * @cfg {String} validClass default "text-success fa fa-lg fa-check"
29574 * @cfg {String} iconTooltip default "This field is required"
29577 * Create a new FieldLabel
29578 * @param {Object} config The config object
29581 Roo.bootstrap.FieldLabel = function(config){
29582 Roo.bootstrap.Element.superclass.constructor.call(this, config);
29587 * Fires after the field has been marked as invalid.
29588 * @param {Roo.form.FieldLabel} this
29589 * @param {String} msg The validation message
29594 * Fires after the field has been validated with no errors.
29595 * @param {Roo.form.FieldLabel} this
29601 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
29608 invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
29609 validClass : 'text-success fa fa-lg fa-check',
29610 iconTooltip : 'This field is required',
29612 getAutoCreate : function(){
29616 cls : 'roo-bootstrap-field-label ' + this.cls,
29622 tooltip : this.iconTooltip
29634 initEvents: function()
29636 Roo.bootstrap.Element.superclass.initEvents.call(this);
29638 this.iconEl = this.el.select('i', true).first();
29640 this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
29642 Roo.bootstrap.FieldLabel.register(this);
29646 * Mark this field as valid
29648 markValid : function()
29650 this.iconEl.show();
29652 this.iconEl.removeClass(this.invalidClass);
29654 this.iconEl.addClass(this.validClass);
29656 this.fireEvent('valid', this);
29660 * Mark this field as invalid
29661 * @param {String} msg The validation message
29663 markInvalid : function(msg)
29665 this.iconEl.show();
29667 this.iconEl.removeClass(this.validClass);
29669 this.iconEl.addClass(this.invalidClass);
29671 this.fireEvent('invalid', this, msg);
29677 Roo.apply(Roo.bootstrap.FieldLabel, {
29682 * register a FieldLabel Group
29683 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
29685 register : function(label)
29687 if(this.groups.hasOwnProperty(label.target)){
29691 this.groups[label.target] = label;
29695 * fetch a FieldLabel Group based on the target
29696 * @param {string} target
29697 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
29699 get: function(target) {
29700 if (typeof(this.groups[target]) == 'undefined') {
29704 return this.groups[target] ;
29713 * page DateSplitField.
29719 * @class Roo.bootstrap.DateSplitField
29720 * @extends Roo.bootstrap.Component
29721 * Bootstrap DateSplitField class
29722 * @cfg {string} fieldLabel - the label associated
29723 * @cfg {Number} labelWidth set the width of label (0-12)
29724 * @cfg {String} labelAlign (top|left)
29725 * @cfg {Boolean} dayAllowBlank (true|false) default false
29726 * @cfg {Boolean} monthAllowBlank (true|false) default false
29727 * @cfg {Boolean} yearAllowBlank (true|false) default false
29728 * @cfg {string} dayPlaceholder
29729 * @cfg {string} monthPlaceholder
29730 * @cfg {string} yearPlaceholder
29731 * @cfg {string} dayFormat default 'd'
29732 * @cfg {string} monthFormat default 'm'
29733 * @cfg {string} yearFormat default 'Y'
29734 * @cfg {Number} labellg set the width of label (1-12)
29735 * @cfg {Number} labelmd set the width of label (1-12)
29736 * @cfg {Number} labelsm set the width of label (1-12)
29737 * @cfg {Number} labelxs set the width of label (1-12)
29741 * Create a new DateSplitField
29742 * @param {Object} config The config object
29745 Roo.bootstrap.DateSplitField = function(config){
29746 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
29752 * getting the data of years
29753 * @param {Roo.bootstrap.DateSplitField} this
29754 * @param {Object} years
29759 * getting the data of days
29760 * @param {Roo.bootstrap.DateSplitField} this
29761 * @param {Object} days
29766 * Fires after the field has been marked as invalid.
29767 * @param {Roo.form.Field} this
29768 * @param {String} msg The validation message
29773 * Fires after the field has been validated with no errors.
29774 * @param {Roo.form.Field} this
29780 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
29783 labelAlign : 'top',
29785 dayAllowBlank : false,
29786 monthAllowBlank : false,
29787 yearAllowBlank : false,
29788 dayPlaceholder : '',
29789 monthPlaceholder : '',
29790 yearPlaceholder : '',
29794 isFormField : true,
29800 getAutoCreate : function()
29804 cls : 'row roo-date-split-field-group',
29809 cls : 'form-hidden-field roo-date-split-field-group-value',
29815 var labelCls = 'col-md-12';
29816 var contentCls = 'col-md-4';
29818 if(this.fieldLabel){
29822 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
29826 html : this.fieldLabel
29831 if(this.labelAlign == 'left'){
29833 if(this.labelWidth > 12){
29834 label.style = "width: " + this.labelWidth + 'px';
29837 if(this.labelWidth < 13 && this.labelmd == 0){
29838 this.labelmd = this.labelWidth;
29841 if(this.labellg > 0){
29842 labelCls = ' col-lg-' + this.labellg;
29843 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
29846 if(this.labelmd > 0){
29847 labelCls = ' col-md-' + this.labelmd;
29848 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
29851 if(this.labelsm > 0){
29852 labelCls = ' col-sm-' + this.labelsm;
29853 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
29856 if(this.labelxs > 0){
29857 labelCls = ' col-xs-' + this.labelxs;
29858 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
29862 label.cls += ' ' + labelCls;
29864 cfg.cn.push(label);
29867 Roo.each(['day', 'month', 'year'], function(t){
29870 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
29877 inputEl: function ()
29879 return this.el.select('.roo-date-split-field-group-value', true).first();
29882 onRender : function(ct, position)
29886 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29888 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
29890 this.dayField = new Roo.bootstrap.ComboBox({
29891 allowBlank : this.dayAllowBlank,
29892 alwaysQuery : true,
29893 displayField : 'value',
29896 forceSelection : true,
29898 placeholder : this.dayPlaceholder,
29899 selectOnFocus : true,
29900 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29901 triggerAction : 'all',
29903 valueField : 'value',
29904 store : new Roo.data.SimpleStore({
29905 data : (function() {
29907 _this.fireEvent('days', _this, days);
29910 fields : [ 'value' ]
29913 select : function (_self, record, index)
29915 _this.setValue(_this.getValue());
29920 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
29922 this.monthField = new Roo.bootstrap.MonthField({
29923 after : '<i class=\"fa fa-calendar\"></i>',
29924 allowBlank : this.monthAllowBlank,
29925 placeholder : this.monthPlaceholder,
29928 render : function (_self)
29930 this.el.select('span.input-group-addon', true).first().on('click', function(e){
29931 e.preventDefault();
29935 select : function (_self, oldvalue, newvalue)
29937 _this.setValue(_this.getValue());
29942 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
29944 this.yearField = new Roo.bootstrap.ComboBox({
29945 allowBlank : this.yearAllowBlank,
29946 alwaysQuery : true,
29947 displayField : 'value',
29950 forceSelection : true,
29952 placeholder : this.yearPlaceholder,
29953 selectOnFocus : true,
29954 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29955 triggerAction : 'all',
29957 valueField : 'value',
29958 store : new Roo.data.SimpleStore({
29959 data : (function() {
29961 _this.fireEvent('years', _this, years);
29964 fields : [ 'value' ]
29967 select : function (_self, record, index)
29969 _this.setValue(_this.getValue());
29974 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
29977 setValue : function(v, format)
29979 this.inputEl.dom.value = v;
29981 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
29983 var d = Date.parseDate(v, f);
29990 this.setDay(d.format(this.dayFormat));
29991 this.setMonth(d.format(this.monthFormat));
29992 this.setYear(d.format(this.yearFormat));
29999 setDay : function(v)
30001 this.dayField.setValue(v);
30002 this.inputEl.dom.value = this.getValue();
30007 setMonth : function(v)
30009 this.monthField.setValue(v, true);
30010 this.inputEl.dom.value = this.getValue();
30015 setYear : function(v)
30017 this.yearField.setValue(v);
30018 this.inputEl.dom.value = this.getValue();
30023 getDay : function()
30025 return this.dayField.getValue();
30028 getMonth : function()
30030 return this.monthField.getValue();
30033 getYear : function()
30035 return this.yearField.getValue();
30038 getValue : function()
30040 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30042 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30052 this.inputEl.dom.value = '';
30057 validate : function()
30059 var d = this.dayField.validate();
30060 var m = this.monthField.validate();
30061 var y = this.yearField.validate();
30066 (!this.dayAllowBlank && !d) ||
30067 (!this.monthAllowBlank && !m) ||
30068 (!this.yearAllowBlank && !y)
30073 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30082 this.markInvalid();
30087 markValid : function()
30090 var label = this.el.select('label', true).first();
30091 var icon = this.el.select('i.fa-star', true).first();
30097 this.fireEvent('valid', this);
30101 * Mark this field as invalid
30102 * @param {String} msg The validation message
30104 markInvalid : function(msg)
30107 var label = this.el.select('label', true).first();
30108 var icon = this.el.select('i.fa-star', true).first();
30110 if(label && !icon){
30111 this.el.select('.roo-date-split-field-label', true).createChild({
30113 cls : 'text-danger fa fa-lg fa-star',
30114 tooltip : 'This field is required',
30115 style : 'margin-right:5px;'
30119 this.fireEvent('invalid', this, msg);
30122 clearInvalid : function()
30124 var label = this.el.select('label', true).first();
30125 var icon = this.el.select('i.fa-star', true).first();
30131 this.fireEvent('valid', this);
30134 getName: function()
30144 * http://masonry.desandro.com
30146 * The idea is to render all the bricks based on vertical width...
30148 * The original code extends 'outlayer' - we might need to use that....
30154 * @class Roo.bootstrap.LayoutMasonry
30155 * @extends Roo.bootstrap.Component
30156 * Bootstrap Layout Masonry class
30159 * Create a new Element
30160 * @param {Object} config The config object
30163 Roo.bootstrap.LayoutMasonry = function(config){
30165 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30169 Roo.bootstrap.LayoutMasonry.register(this);
30175 * Fire after layout the items
30176 * @param {Roo.bootstrap.LayoutMasonry} this
30177 * @param {Roo.EventObject} e
30184 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
30187 * @cfg {Boolean} isLayoutInstant = no animation?
30189 isLayoutInstant : false, // needed?
30192 * @cfg {Number} boxWidth width of the columns
30197 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
30202 * @cfg {Number} padWidth padding below box..
30207 * @cfg {Number} gutter gutter width..
30212 * @cfg {Number} maxCols maximum number of columns
30218 * @cfg {Boolean} isAutoInitial defalut true
30220 isAutoInitial : true,
30225 * @cfg {Boolean} isHorizontal defalut false
30227 isHorizontal : false,
30229 currentSize : null,
30235 bricks: null, //CompositeElement
30239 _isLayoutInited : false,
30241 // isAlternative : false, // only use for vertical layout...
30244 * @cfg {Number} alternativePadWidth padding below box..
30246 alternativePadWidth : 50,
30248 selectedBrick : [],
30250 getAutoCreate : function(){
30252 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30256 cls: 'blog-masonary-wrapper ' + this.cls,
30258 cls : 'mas-boxes masonary'
30265 getChildContainer: function( )
30267 if (this.boxesEl) {
30268 return this.boxesEl;
30271 this.boxesEl = this.el.select('.mas-boxes').first();
30273 return this.boxesEl;
30277 initEvents : function()
30281 if(this.isAutoInitial){
30282 Roo.log('hook children rendered');
30283 this.on('childrenrendered', function() {
30284 Roo.log('children rendered');
30290 initial : function()
30292 this.selectedBrick = [];
30294 this.currentSize = this.el.getBox(true);
30296 Roo.EventManager.onWindowResize(this.resize, this);
30298 if(!this.isAutoInitial){
30306 //this.layout.defer(500,this);
30310 resize : function()
30312 var cs = this.el.getBox(true);
30315 this.currentSize.width == cs.width &&
30316 this.currentSize.x == cs.x &&
30317 this.currentSize.height == cs.height &&
30318 this.currentSize.y == cs.y
30320 Roo.log("no change in with or X or Y");
30324 this.currentSize = cs;
30330 layout : function()
30332 this._resetLayout();
30334 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30336 this.layoutItems( isInstant );
30338 this._isLayoutInited = true;
30340 this.fireEvent('layout', this);
30344 _resetLayout : function()
30346 if(this.isHorizontal){
30347 this.horizontalMeasureColumns();
30351 this.verticalMeasureColumns();
30355 verticalMeasureColumns : function()
30357 this.getContainerWidth();
30359 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30360 // this.colWidth = Math.floor(this.containerWidth * 0.8);
30364 var boxWidth = this.boxWidth + this.padWidth;
30366 if(this.containerWidth < this.boxWidth){
30367 boxWidth = this.containerWidth
30370 var containerWidth = this.containerWidth;
30372 var cols = Math.floor(containerWidth / boxWidth);
30374 this.cols = Math.max( cols, 1 );
30376 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30378 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30380 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30382 this.colWidth = boxWidth + avail - this.padWidth;
30384 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30385 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
30388 horizontalMeasureColumns : function()
30390 this.getContainerWidth();
30392 var boxWidth = this.boxWidth;
30394 if(this.containerWidth < boxWidth){
30395 boxWidth = this.containerWidth;
30398 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30400 this.el.setHeight(boxWidth);
30404 getContainerWidth : function()
30406 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
30409 layoutItems : function( isInstant )
30411 Roo.log(this.bricks);
30413 var items = Roo.apply([], this.bricks);
30415 if(this.isHorizontal){
30416 this._horizontalLayoutItems( items , isInstant );
30420 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30421 // this._verticalAlternativeLayoutItems( items , isInstant );
30425 this._verticalLayoutItems( items , isInstant );
30429 _verticalLayoutItems : function ( items , isInstant)
30431 if ( !items || !items.length ) {
30436 ['xs', 'xs', 'xs', 'tall'],
30437 ['xs', 'xs', 'tall'],
30438 ['xs', 'xs', 'sm'],
30439 ['xs', 'xs', 'xs'],
30445 ['sm', 'xs', 'xs'],
30449 ['tall', 'xs', 'xs', 'xs'],
30450 ['tall', 'xs', 'xs'],
30462 Roo.each(items, function(item, k){
30464 switch (item.size) {
30465 // these layouts take up a full box,
30476 boxes.push([item]);
30499 var filterPattern = function(box, length)
30507 var pattern = box.slice(0, length);
30511 Roo.each(pattern, function(i){
30512 format.push(i.size);
30515 Roo.each(standard, function(s){
30517 if(String(s) != String(format)){
30526 if(!match && length == 1){
30531 filterPattern(box, length - 1);
30535 queue.push(pattern);
30537 box = box.slice(length, box.length);
30539 filterPattern(box, 4);
30545 Roo.each(boxes, function(box, k){
30551 if(box.length == 1){
30556 filterPattern(box, 4);
30560 this._processVerticalLayoutQueue( queue, isInstant );
30564 // _verticalAlternativeLayoutItems : function( items , isInstant )
30566 // if ( !items || !items.length ) {
30570 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
30574 _horizontalLayoutItems : function ( items , isInstant)
30576 if ( !items || !items.length || items.length < 3) {
30582 var eItems = items.slice(0, 3);
30584 items = items.slice(3, items.length);
30587 ['xs', 'xs', 'xs', 'wide'],
30588 ['xs', 'xs', 'wide'],
30589 ['xs', 'xs', 'sm'],
30590 ['xs', 'xs', 'xs'],
30596 ['sm', 'xs', 'xs'],
30600 ['wide', 'xs', 'xs', 'xs'],
30601 ['wide', 'xs', 'xs'],
30614 Roo.each(items, function(item, k){
30616 switch (item.size) {
30627 boxes.push([item]);
30651 var filterPattern = function(box, length)
30659 var pattern = box.slice(0, length);
30663 Roo.each(pattern, function(i){
30664 format.push(i.size);
30667 Roo.each(standard, function(s){
30669 if(String(s) != String(format)){
30678 if(!match && length == 1){
30683 filterPattern(box, length - 1);
30687 queue.push(pattern);
30689 box = box.slice(length, box.length);
30691 filterPattern(box, 4);
30697 Roo.each(boxes, function(box, k){
30703 if(box.length == 1){
30708 filterPattern(box, 4);
30715 var pos = this.el.getBox(true);
30719 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30721 var hit_end = false;
30723 Roo.each(queue, function(box){
30727 Roo.each(box, function(b){
30729 b.el.setVisibilityMode(Roo.Element.DISPLAY);
30739 Roo.each(box, function(b){
30741 b.el.setVisibilityMode(Roo.Element.DISPLAY);
30744 mx = Math.max(mx, b.x);
30748 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
30752 Roo.each(box, function(b){
30754 b.el.setVisibilityMode(Roo.Element.DISPLAY);
30768 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
30771 /** Sets position of item in DOM
30772 * @param {Element} item
30773 * @param {Number} x - horizontal position
30774 * @param {Number} y - vertical position
30775 * @param {Boolean} isInstant - disables transitions
30777 _processVerticalLayoutQueue : function( queue, isInstant )
30779 var pos = this.el.getBox(true);
30784 for (var i = 0; i < this.cols; i++){
30788 Roo.each(queue, function(box, k){
30790 var col = k % this.cols;
30792 Roo.each(box, function(b,kk){
30794 b.el.position('absolute');
30796 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30797 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30799 if(b.size == 'md-left' || b.size == 'md-right'){
30800 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30801 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30804 b.el.setWidth(width);
30805 b.el.setHeight(height);
30807 b.el.select('iframe',true).setSize(width,height);
30811 for (var i = 0; i < this.cols; i++){
30813 if(maxY[i] < maxY[col]){
30818 col = Math.min(col, i);
30822 x = pos.x + col * (this.colWidth + this.padWidth);
30826 var positions = [];
30828 switch (box.length){
30830 positions = this.getVerticalOneBoxColPositions(x, y, box);
30833 positions = this.getVerticalTwoBoxColPositions(x, y, box);
30836 positions = this.getVerticalThreeBoxColPositions(x, y, box);
30839 positions = this.getVerticalFourBoxColPositions(x, y, box);
30845 Roo.each(box, function(b,kk){
30847 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30849 var sz = b.el.getSize();
30851 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
30859 for (var i = 0; i < this.cols; i++){
30860 mY = Math.max(mY, maxY[i]);
30863 this.el.setHeight(mY - pos.y);
30867 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
30869 // var pos = this.el.getBox(true);
30872 // var maxX = pos.right;
30874 // var maxHeight = 0;
30876 // Roo.each(items, function(item, k){
30880 // item.el.position('absolute');
30882 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
30884 // item.el.setWidth(width);
30886 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
30888 // item.el.setHeight(height);
30891 // item.el.setXY([x, y], isInstant ? false : true);
30893 // item.el.setXY([maxX - width, y], isInstant ? false : true);
30896 // y = y + height + this.alternativePadWidth;
30898 // maxHeight = maxHeight + height + this.alternativePadWidth;
30902 // this.el.setHeight(maxHeight);
30906 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
30908 var pos = this.el.getBox(true);
30913 var maxX = pos.right;
30915 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
30917 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30919 Roo.each(queue, function(box, k){
30921 Roo.each(box, function(b, kk){
30923 b.el.position('absolute');
30925 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30926 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30928 if(b.size == 'md-left' || b.size == 'md-right'){
30929 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30930 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30933 b.el.setWidth(width);
30934 b.el.setHeight(height);
30942 var positions = [];
30944 switch (box.length){
30946 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
30949 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
30952 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
30955 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
30961 Roo.each(box, function(b,kk){
30963 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30965 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
30973 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
30975 Roo.each(eItems, function(b,k){
30977 b.size = (k == 0) ? 'sm' : 'xs';
30978 b.x = (k == 0) ? 2 : 1;
30979 b.y = (k == 0) ? 2 : 1;
30981 b.el.position('absolute');
30983 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30985 b.el.setWidth(width);
30987 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30989 b.el.setHeight(height);
30993 var positions = [];
30996 x : maxX - this.unitWidth * 2 - this.gutter,
31001 x : maxX - this.unitWidth,
31002 y : minY + (this.unitWidth + this.gutter) * 2
31006 x : maxX - this.unitWidth * 3 - this.gutter * 2,
31010 Roo.each(eItems, function(b,k){
31012 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31018 getVerticalOneBoxColPositions : function(x, y, box)
31022 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31024 if(box[0].size == 'md-left'){
31028 if(box[0].size == 'md-right'){
31033 x : x + (this.unitWidth + this.gutter) * rand,
31040 getVerticalTwoBoxColPositions : function(x, y, box)
31044 if(box[0].size == 'xs'){
31048 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31052 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31066 x : x + (this.unitWidth + this.gutter) * 2,
31067 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31074 getVerticalThreeBoxColPositions : function(x, y, box)
31078 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31086 x : x + (this.unitWidth + this.gutter) * 1,
31091 x : x + (this.unitWidth + this.gutter) * 2,
31099 if(box[0].size == 'xs' && box[1].size == 'xs'){
31108 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31112 x : x + (this.unitWidth + this.gutter) * 1,
31126 x : x + (this.unitWidth + this.gutter) * 2,
31131 x : x + (this.unitWidth + this.gutter) * 2,
31132 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31139 getVerticalFourBoxColPositions : function(x, y, box)
31143 if(box[0].size == 'xs'){
31152 y : y + (this.unitHeight + this.gutter) * 1
31157 y : y + (this.unitHeight + this.gutter) * 2
31161 x : x + (this.unitWidth + this.gutter) * 1,
31175 x : x + (this.unitWidth + this.gutter) * 2,
31180 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31181 y : y + (this.unitHeight + this.gutter) * 1
31185 x : x + (this.unitWidth + this.gutter) * 2,
31186 y : y + (this.unitWidth + this.gutter) * 2
31193 getHorizontalOneBoxColPositions : function(maxX, minY, box)
31197 if(box[0].size == 'md-left'){
31199 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31206 if(box[0].size == 'md-right'){
31208 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31209 y : minY + (this.unitWidth + this.gutter) * 1
31215 var rand = Math.floor(Math.random() * (4 - box[0].y));
31218 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31219 y : minY + (this.unitWidth + this.gutter) * rand
31226 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31230 if(box[0].size == 'xs'){
31233 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31238 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31239 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31247 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31252 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31253 y : minY + (this.unitWidth + this.gutter) * 2
31260 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31264 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31267 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31272 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31273 y : minY + (this.unitWidth + this.gutter) * 1
31277 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31278 y : minY + (this.unitWidth + this.gutter) * 2
31285 if(box[0].size == 'xs' && box[1].size == 'xs'){
31288 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31293 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31298 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31299 y : minY + (this.unitWidth + this.gutter) * 1
31307 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31312 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31313 y : minY + (this.unitWidth + this.gutter) * 2
31317 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31318 y : minY + (this.unitWidth + this.gutter) * 2
31325 getHorizontalFourBoxColPositions : function(maxX, minY, box)
31329 if(box[0].size == 'xs'){
31332 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31337 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31342 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),
31347 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31348 y : minY + (this.unitWidth + this.gutter) * 1
31356 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31361 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31362 y : minY + (this.unitWidth + this.gutter) * 2
31366 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31367 y : minY + (this.unitWidth + this.gutter) * 2
31371 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),
31372 y : minY + (this.unitWidth + this.gutter) * 2
31380 * remove a Masonry Brick
31381 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31383 removeBrick : function(brick_id)
31389 for (var i = 0; i<this.bricks.length; i++) {
31390 if (this.bricks[i].id == brick_id) {
31391 this.bricks.splice(i,1);
31392 this.el.dom.removeChild(Roo.get(brick_id).dom);
31399 * adds a Masonry Brick
31400 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31402 addBrick : function(cfg)
31404 var cn = new Roo.bootstrap.MasonryBrick(cfg);
31405 //this.register(cn);
31406 cn.parentId = this.id;
31407 cn.onRender(this.el, null);
31412 * register a Masonry Brick
31413 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31416 register : function(brick)
31418 this.bricks.push(brick);
31419 brick.masonryId = this.id;
31423 * clear all the Masonry Brick
31425 clearAll : function()
31428 //this.getChildContainer().dom.innerHTML = "";
31429 this.el.dom.innerHTML = '';
31432 getSelected : function()
31434 if (!this.selectedBrick) {
31438 return this.selectedBrick;
31442 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31446 * register a Masonry Layout
31447 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31450 register : function(layout)
31452 this.groups[layout.id] = layout;
31455 * fetch a Masonry Layout based on the masonry layout ID
31456 * @param {string} the masonry layout to add
31457 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31460 get: function(layout_id) {
31461 if (typeof(this.groups[layout_id]) == 'undefined') {
31464 return this.groups[layout_id] ;
31476 * http://masonry.desandro.com
31478 * The idea is to render all the bricks based on vertical width...
31480 * The original code extends 'outlayer' - we might need to use that....
31486 * @class Roo.bootstrap.LayoutMasonryAuto
31487 * @extends Roo.bootstrap.Component
31488 * Bootstrap Layout Masonry class
31491 * Create a new Element
31492 * @param {Object} config The config object
31495 Roo.bootstrap.LayoutMasonryAuto = function(config){
31496 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31499 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
31502 * @cfg {Boolean} isFitWidth - resize the width..
31504 isFitWidth : false, // options..
31506 * @cfg {Boolean} isOriginLeft = left align?
31508 isOriginLeft : true,
31510 * @cfg {Boolean} isOriginTop = top align?
31512 isOriginTop : false,
31514 * @cfg {Boolean} isLayoutInstant = no animation?
31516 isLayoutInstant : false, // needed?
31518 * @cfg {Boolean} isResizingContainer = not sure if this is used..
31520 isResizingContainer : true,
31522 * @cfg {Number} columnWidth width of the columns
31528 * @cfg {Number} maxCols maximum number of columns
31533 * @cfg {Number} padHeight padding below box..
31539 * @cfg {Boolean} isAutoInitial defalut true
31542 isAutoInitial : true,
31548 initialColumnWidth : 0,
31549 currentSize : null,
31551 colYs : null, // array.
31558 bricks: null, //CompositeElement
31559 cols : 0, // array?
31560 // element : null, // wrapped now this.el
31561 _isLayoutInited : null,
31564 getAutoCreate : function(){
31568 cls: 'blog-masonary-wrapper ' + this.cls,
31570 cls : 'mas-boxes masonary'
31577 getChildContainer: function( )
31579 if (this.boxesEl) {
31580 return this.boxesEl;
31583 this.boxesEl = this.el.select('.mas-boxes').first();
31585 return this.boxesEl;
31589 initEvents : function()
31593 if(this.isAutoInitial){
31594 Roo.log('hook children rendered');
31595 this.on('childrenrendered', function() {
31596 Roo.log('children rendered');
31603 initial : function()
31605 this.reloadItems();
31607 this.currentSize = this.el.getBox(true);
31609 /// was window resize... - let's see if this works..
31610 Roo.EventManager.onWindowResize(this.resize, this);
31612 if(!this.isAutoInitial){
31617 this.layout.defer(500,this);
31620 reloadItems: function()
31622 this.bricks = this.el.select('.masonry-brick', true);
31624 this.bricks.each(function(b) {
31625 //Roo.log(b.getSize());
31626 if (!b.attr('originalwidth')) {
31627 b.attr('originalwidth', b.getSize().width);
31632 Roo.log(this.bricks.elements.length);
31635 resize : function()
31638 var cs = this.el.getBox(true);
31640 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
31641 Roo.log("no change in with or X");
31644 this.currentSize = cs;
31648 layout : function()
31651 this._resetLayout();
31652 //this._manageStamps();
31654 // don't animate first layout
31655 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31656 this.layoutItems( isInstant );
31658 // flag for initalized
31659 this._isLayoutInited = true;
31662 layoutItems : function( isInstant )
31664 //var items = this._getItemsForLayout( this.items );
31665 // original code supports filtering layout items.. we just ignore it..
31667 this._layoutItems( this.bricks , isInstant );
31669 this._postLayout();
31671 _layoutItems : function ( items , isInstant)
31673 //this.fireEvent( 'layout', this, items );
31676 if ( !items || !items.elements.length ) {
31677 // no items, emit event with empty array
31682 items.each(function(item) {
31683 Roo.log("layout item");
31685 // get x/y object from method
31686 var position = this._getItemLayoutPosition( item );
31688 position.item = item;
31689 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
31690 queue.push( position );
31693 this._processLayoutQueue( queue );
31695 /** Sets position of item in DOM
31696 * @param {Element} item
31697 * @param {Number} x - horizontal position
31698 * @param {Number} y - vertical position
31699 * @param {Boolean} isInstant - disables transitions
31701 _processLayoutQueue : function( queue )
31703 for ( var i=0, len = queue.length; i < len; i++ ) {
31704 var obj = queue[i];
31705 obj.item.position('absolute');
31706 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
31712 * Any logic you want to do after each layout,
31713 * i.e. size the container
31715 _postLayout : function()
31717 this.resizeContainer();
31720 resizeContainer : function()
31722 if ( !this.isResizingContainer ) {
31725 var size = this._getContainerSize();
31727 this.el.setSize(size.width,size.height);
31728 this.boxesEl.setSize(size.width,size.height);
31734 _resetLayout : function()
31736 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
31737 this.colWidth = this.el.getWidth();
31738 //this.gutter = this.el.getWidth();
31740 this.measureColumns();
31746 this.colYs.push( 0 );
31752 measureColumns : function()
31754 this.getContainerWidth();
31755 // if columnWidth is 0, default to outerWidth of first item
31756 if ( !this.columnWidth ) {
31757 var firstItem = this.bricks.first();
31758 Roo.log(firstItem);
31759 this.columnWidth = this.containerWidth;
31760 if (firstItem && firstItem.attr('originalwidth') ) {
31761 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
31763 // columnWidth fall back to item of first element
31764 Roo.log("set column width?");
31765 this.initialColumnWidth = this.columnWidth ;
31767 // if first elem has no width, default to size of container
31772 if (this.initialColumnWidth) {
31773 this.columnWidth = this.initialColumnWidth;
31778 // column width is fixed at the top - however if container width get's smaller we should
31781 // this bit calcs how man columns..
31783 var columnWidth = this.columnWidth += this.gutter;
31785 // calculate columns
31786 var containerWidth = this.containerWidth + this.gutter;
31788 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
31789 // fix rounding errors, typically with gutters
31790 var excess = columnWidth - containerWidth % columnWidth;
31793 // if overshoot is less than a pixel, round up, otherwise floor it
31794 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
31795 cols = Math[ mathMethod ]( cols );
31796 this.cols = Math.max( cols, 1 );
31797 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31799 // padding positioning..
31800 var totalColWidth = this.cols * this.columnWidth;
31801 var padavail = this.containerWidth - totalColWidth;
31802 // so for 2 columns - we need 3 'pads'
31804 var padNeeded = (1+this.cols) * this.padWidth;
31806 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
31808 this.columnWidth += padExtra
31809 //this.padWidth = Math.floor(padavail / ( this.cols));
31811 // adjust colum width so that padding is fixed??
31813 // we have 3 columns ... total = width * 3
31814 // we have X left over... that should be used by
31816 //if (this.expandC) {
31824 getContainerWidth : function()
31826 /* // container is parent if fit width
31827 var container = this.isFitWidth ? this.element.parentNode : this.element;
31828 // check that this.size and size are there
31829 // IE8 triggers resize on body size change, so they might not be
31831 var size = getSize( container ); //FIXME
31832 this.containerWidth = size && size.innerWidth; //FIXME
31835 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
31839 _getItemLayoutPosition : function( item ) // what is item?
31841 // we resize the item to our columnWidth..
31843 item.setWidth(this.columnWidth);
31844 item.autoBoxAdjust = false;
31846 var sz = item.getSize();
31848 // how many columns does this brick span
31849 var remainder = this.containerWidth % this.columnWidth;
31851 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
31852 // round if off by 1 pixel, otherwise use ceil
31853 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
31854 colSpan = Math.min( colSpan, this.cols );
31856 // normally this should be '1' as we dont' currently allow multi width columns..
31858 var colGroup = this._getColGroup( colSpan );
31859 // get the minimum Y value from the columns
31860 var minimumY = Math.min.apply( Math, colGroup );
31861 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
31863 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
31865 // position the brick
31867 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
31868 y: this.currentSize.y + minimumY + this.padHeight
31872 // apply setHeight to necessary columns
31873 var setHeight = minimumY + sz.height + this.padHeight;
31874 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
31876 var setSpan = this.cols + 1 - colGroup.length;
31877 for ( var i = 0; i < setSpan; i++ ) {
31878 this.colYs[ shortColIndex + i ] = setHeight ;
31885 * @param {Number} colSpan - number of columns the element spans
31886 * @returns {Array} colGroup
31888 _getColGroup : function( colSpan )
31890 if ( colSpan < 2 ) {
31891 // if brick spans only one column, use all the column Ys
31896 // how many different places could this brick fit horizontally
31897 var groupCount = this.cols + 1 - colSpan;
31898 // for each group potential horizontal position
31899 for ( var i = 0; i < groupCount; i++ ) {
31900 // make an array of colY values for that one group
31901 var groupColYs = this.colYs.slice( i, i + colSpan );
31902 // and get the max value of the array
31903 colGroup[i] = Math.max.apply( Math, groupColYs );
31908 _manageStamp : function( stamp )
31910 var stampSize = stamp.getSize();
31911 var offset = stamp.getBox();
31912 // get the columns that this stamp affects
31913 var firstX = this.isOriginLeft ? offset.x : offset.right;
31914 var lastX = firstX + stampSize.width;
31915 var firstCol = Math.floor( firstX / this.columnWidth );
31916 firstCol = Math.max( 0, firstCol );
31918 var lastCol = Math.floor( lastX / this.columnWidth );
31919 // lastCol should not go over if multiple of columnWidth #425
31920 lastCol -= lastX % this.columnWidth ? 0 : 1;
31921 lastCol = Math.min( this.cols - 1, lastCol );
31923 // set colYs to bottom of the stamp
31924 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
31927 for ( var i = firstCol; i <= lastCol; i++ ) {
31928 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
31933 _getContainerSize : function()
31935 this.maxY = Math.max.apply( Math, this.colYs );
31940 if ( this.isFitWidth ) {
31941 size.width = this._getContainerFitWidth();
31947 _getContainerFitWidth : function()
31949 var unusedCols = 0;
31950 // count unused columns
31953 if ( this.colYs[i] !== 0 ) {
31958 // fit container to columns that have been used
31959 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
31962 needsResizeLayout : function()
31964 var previousWidth = this.containerWidth;
31965 this.getContainerWidth();
31966 return previousWidth !== this.containerWidth;
31981 * @class Roo.bootstrap.MasonryBrick
31982 * @extends Roo.bootstrap.Component
31983 * Bootstrap MasonryBrick class
31986 * Create a new MasonryBrick
31987 * @param {Object} config The config object
31990 Roo.bootstrap.MasonryBrick = function(config){
31992 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
31994 Roo.bootstrap.MasonryBrick.register(this);
32000 * When a MasonryBrick is clcik
32001 * @param {Roo.bootstrap.MasonryBrick} this
32002 * @param {Roo.EventObject} e
32008 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
32011 * @cfg {String} title
32015 * @cfg {String} html
32019 * @cfg {String} bgimage
32023 * @cfg {String} videourl
32027 * @cfg {String} cls
32031 * @cfg {String} href
32035 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32040 * @cfg {String} placetitle (center|bottom)
32045 * @cfg {Boolean} isFitContainer defalut true
32047 isFitContainer : true,
32050 * @cfg {Boolean} preventDefault defalut false
32052 preventDefault : false,
32055 * @cfg {Boolean} inverse defalut false
32057 maskInverse : false,
32059 getAutoCreate : function()
32061 if(!this.isFitContainer){
32062 return this.getSplitAutoCreate();
32065 var cls = 'masonry-brick masonry-brick-full';
32067 if(this.href.length){
32068 cls += ' masonry-brick-link';
32071 if(this.bgimage.length){
32072 cls += ' masonry-brick-image';
32075 if(this.maskInverse){
32076 cls += ' mask-inverse';
32079 if(!this.html.length && !this.maskInverse && !this.videourl.length){
32080 cls += ' enable-mask';
32084 cls += ' masonry-' + this.size + '-brick';
32087 if(this.placetitle.length){
32089 switch (this.placetitle) {
32091 cls += ' masonry-center-title';
32094 cls += ' masonry-bottom-title';
32101 if(!this.html.length && !this.bgimage.length){
32102 cls += ' masonry-center-title';
32105 if(!this.html.length && this.bgimage.length){
32106 cls += ' masonry-bottom-title';
32111 cls += ' ' + this.cls;
32115 tag: (this.href.length) ? 'a' : 'div',
32120 cls: 'masonry-brick-mask'
32124 cls: 'masonry-brick-paragraph',
32130 if(this.href.length){
32131 cfg.href = this.href;
32134 var cn = cfg.cn[1].cn;
32136 if(this.title.length){
32139 cls: 'masonry-brick-title',
32144 if(this.html.length){
32147 cls: 'masonry-brick-text',
32152 if (!this.title.length && !this.html.length) {
32153 cfg.cn[1].cls += ' hide';
32156 if(this.bgimage.length){
32159 cls: 'masonry-brick-image-view',
32164 if(this.videourl.length){
32165 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32166 // youtube support only?
32169 cls: 'masonry-brick-image-view',
32172 allowfullscreen : true
32180 getSplitAutoCreate : function()
32182 var cls = 'masonry-brick masonry-brick-split';
32184 if(this.href.length){
32185 cls += ' masonry-brick-link';
32188 if(this.bgimage.length){
32189 cls += ' masonry-brick-image';
32193 cls += ' masonry-' + this.size + '-brick';
32196 switch (this.placetitle) {
32198 cls += ' masonry-center-title';
32201 cls += ' masonry-bottom-title';
32204 if(!this.bgimage.length){
32205 cls += ' masonry-center-title';
32208 if(this.bgimage.length){
32209 cls += ' masonry-bottom-title';
32215 cls += ' ' + this.cls;
32219 tag: (this.href.length) ? 'a' : 'div',
32224 cls: 'masonry-brick-split-head',
32228 cls: 'masonry-brick-paragraph',
32235 cls: 'masonry-brick-split-body',
32241 if(this.href.length){
32242 cfg.href = this.href;
32245 if(this.title.length){
32246 cfg.cn[0].cn[0].cn.push({
32248 cls: 'masonry-brick-title',
32253 if(this.html.length){
32254 cfg.cn[1].cn.push({
32256 cls: 'masonry-brick-text',
32261 if(this.bgimage.length){
32262 cfg.cn[0].cn.push({
32264 cls: 'masonry-brick-image-view',
32269 if(this.videourl.length){
32270 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32271 // youtube support only?
32272 cfg.cn[0].cn.cn.push({
32274 cls: 'masonry-brick-image-view',
32277 allowfullscreen : true
32284 initEvents: function()
32286 switch (this.size) {
32319 this.el.on('touchstart', this.onTouchStart, this);
32320 this.el.on('touchmove', this.onTouchMove, this);
32321 this.el.on('touchend', this.onTouchEnd, this);
32322 this.el.on('contextmenu', this.onContextMenu, this);
32324 this.el.on('mouseenter' ,this.enter, this);
32325 this.el.on('mouseleave', this.leave, this);
32326 this.el.on('click', this.onClick, this);
32329 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32330 this.parent().bricks.push(this);
32335 onClick: function(e, el)
32337 var time = this.endTimer - this.startTimer;
32338 // Roo.log(e.preventDefault());
32341 e.preventDefault();
32346 if(!this.preventDefault){
32350 e.preventDefault();
32352 if (this.activcClass != '') {
32353 this.selectBrick();
32356 this.fireEvent('click', this);
32359 enter: function(e, el)
32361 e.preventDefault();
32363 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32367 if(this.bgimage.length && this.html.length){
32368 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32372 leave: function(e, el)
32374 e.preventDefault();
32376 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32380 if(this.bgimage.length && this.html.length){
32381 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32385 onTouchStart: function(e, el)
32387 // e.preventDefault();
32389 this.touchmoved = false;
32391 if(!this.isFitContainer){
32395 if(!this.bgimage.length || !this.html.length){
32399 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32401 this.timer = new Date().getTime();
32405 onTouchMove: function(e, el)
32407 this.touchmoved = true;
32410 onContextMenu : function(e,el)
32412 e.preventDefault();
32413 e.stopPropagation();
32417 onTouchEnd: function(e, el)
32419 // e.preventDefault();
32421 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32428 if(!this.bgimage.length || !this.html.length){
32430 if(this.href.length){
32431 window.location.href = this.href;
32437 if(!this.isFitContainer){
32441 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32443 window.location.href = this.href;
32446 //selection on single brick only
32447 selectBrick : function() {
32449 if (!this.parentId) {
32453 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32454 var index = m.selectedBrick.indexOf(this.id);
32457 m.selectedBrick.splice(index,1);
32458 this.el.removeClass(this.activeClass);
32462 for(var i = 0; i < m.selectedBrick.length; i++) {
32463 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32464 b.el.removeClass(b.activeClass);
32467 m.selectedBrick = [];
32469 m.selectedBrick.push(this.id);
32470 this.el.addClass(this.activeClass);
32476 Roo.apply(Roo.bootstrap.MasonryBrick, {
32479 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32481 * register a Masonry Brick
32482 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32485 register : function(brick)
32487 //this.groups[brick.id] = brick;
32488 this.groups.add(brick.id, brick);
32491 * fetch a masonry brick based on the masonry brick ID
32492 * @param {string} the masonry brick to add
32493 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
32496 get: function(brick_id)
32498 // if (typeof(this.groups[brick_id]) == 'undefined') {
32501 // return this.groups[brick_id] ;
32503 if(this.groups.key(brick_id)) {
32504 return this.groups.key(brick_id);
32522 * @class Roo.bootstrap.Brick
32523 * @extends Roo.bootstrap.Component
32524 * Bootstrap Brick class
32527 * Create a new Brick
32528 * @param {Object} config The config object
32531 Roo.bootstrap.Brick = function(config){
32532 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
32538 * When a Brick is click
32539 * @param {Roo.bootstrap.Brick} this
32540 * @param {Roo.EventObject} e
32546 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
32549 * @cfg {String} title
32553 * @cfg {String} html
32557 * @cfg {String} bgimage
32561 * @cfg {String} cls
32565 * @cfg {String} href
32569 * @cfg {String} video
32573 * @cfg {Boolean} square
32577 getAutoCreate : function()
32579 var cls = 'roo-brick';
32581 if(this.href.length){
32582 cls += ' roo-brick-link';
32585 if(this.bgimage.length){
32586 cls += ' roo-brick-image';
32589 if(!this.html.length && !this.bgimage.length){
32590 cls += ' roo-brick-center-title';
32593 if(!this.html.length && this.bgimage.length){
32594 cls += ' roo-brick-bottom-title';
32598 cls += ' ' + this.cls;
32602 tag: (this.href.length) ? 'a' : 'div',
32607 cls: 'roo-brick-paragraph',
32613 if(this.href.length){
32614 cfg.href = this.href;
32617 var cn = cfg.cn[0].cn;
32619 if(this.title.length){
32622 cls: 'roo-brick-title',
32627 if(this.html.length){
32630 cls: 'roo-brick-text',
32637 if(this.bgimage.length){
32640 cls: 'roo-brick-image-view',
32648 initEvents: function()
32650 if(this.title.length || this.html.length){
32651 this.el.on('mouseenter' ,this.enter, this);
32652 this.el.on('mouseleave', this.leave, this);
32655 Roo.EventManager.onWindowResize(this.resize, this);
32657 if(this.bgimage.length){
32658 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
32659 this.imageEl.on('load', this.onImageLoad, this);
32666 onImageLoad : function()
32671 resize : function()
32673 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
32675 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
32677 if(this.bgimage.length){
32678 var image = this.el.select('.roo-brick-image-view', true).first();
32680 image.setWidth(paragraph.getWidth());
32683 image.setHeight(paragraph.getWidth());
32686 this.el.setHeight(image.getHeight());
32687 paragraph.setHeight(image.getHeight());
32693 enter: function(e, el)
32695 e.preventDefault();
32697 if(this.bgimage.length){
32698 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
32699 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
32703 leave: function(e, el)
32705 e.preventDefault();
32707 if(this.bgimage.length){
32708 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
32709 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
32725 * @class Roo.bootstrap.NumberField
32726 * @extends Roo.bootstrap.Input
32727 * Bootstrap NumberField class
32733 * Create a new NumberField
32734 * @param {Object} config The config object
32737 Roo.bootstrap.NumberField = function(config){
32738 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
32741 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
32744 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
32746 allowDecimals : true,
32748 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
32750 decimalSeparator : ".",
32752 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
32754 decimalPrecision : 2,
32756 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
32758 allowNegative : true,
32760 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
32762 minValue : Number.NEGATIVE_INFINITY,
32764 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
32766 maxValue : Number.MAX_VALUE,
32768 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
32770 minText : "The minimum value for this field is {0}",
32772 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
32774 maxText : "The maximum value for this field is {0}",
32776 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
32777 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
32779 nanText : "{0} is not a valid number",
32781 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
32786 initEvents : function()
32788 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
32790 var allowed = "0123456789";
32792 if(this.allowDecimals){
32793 allowed += this.decimalSeparator;
32796 if(this.allowNegative){
32800 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
32802 var keyPress = function(e){
32804 var k = e.getKey();
32806 var c = e.getCharCode();
32809 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
32810 allowed.indexOf(String.fromCharCode(c)) === -1
32816 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
32820 if(allowed.indexOf(String.fromCharCode(c)) === -1){
32825 this.el.on("keypress", keyPress, this);
32828 validateValue : function(value)
32831 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
32835 var num = this.parseValue(value);
32838 this.markInvalid(String.format(this.nanText, value));
32842 if(num < this.minValue){
32843 this.markInvalid(String.format(this.minText, this.minValue));
32847 if(num > this.maxValue){
32848 this.markInvalid(String.format(this.maxText, this.maxValue));
32855 getValue : function()
32857 return this.fixPrecision(this.parseValue(Roo.bootstrap.NumberField.superclass.getValue.call(this)));
32860 parseValue : function(value)
32862 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
32863 return isNaN(value) ? '' : value;
32866 fixPrecision : function(value)
32868 var nan = isNaN(value);
32870 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
32871 return nan ? '' : value;
32873 return parseFloat(value).toFixed(this.decimalPrecision);
32876 setValue : function(v)
32878 v = this.fixPrecision(v);
32879 Roo.bootstrap.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
32882 decimalPrecisionFcn : function(v)
32884 return Math.floor(v);
32887 beforeBlur : function()
32893 var v = this.parseValue(this.getRawValue());
32908 * @class Roo.bootstrap.DocumentSlider
32909 * @extends Roo.bootstrap.Component
32910 * Bootstrap DocumentSlider class
32913 * Create a new DocumentViewer
32914 * @param {Object} config The config object
32917 Roo.bootstrap.DocumentSlider = function(config){
32918 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
32925 * Fire after initEvent
32926 * @param {Roo.bootstrap.DocumentSlider} this
32931 * Fire after update
32932 * @param {Roo.bootstrap.DocumentSlider} this
32938 * @param {Roo.bootstrap.DocumentSlider} this
32944 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
32950 getAutoCreate : function()
32954 cls : 'roo-document-slider',
32958 cls : 'roo-document-slider-header',
32962 cls : 'roo-document-slider-header-title'
32968 cls : 'roo-document-slider-body',
32972 cls : 'roo-document-slider-prev',
32976 cls : 'fa fa-chevron-left'
32982 cls : 'roo-document-slider-thumb',
32986 cls : 'roo-document-slider-image'
32992 cls : 'roo-document-slider-next',
32996 cls : 'fa fa-chevron-right'
33008 initEvents : function()
33010 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33011 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33013 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33014 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33016 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33017 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33019 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33020 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33022 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33023 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33025 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33026 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33028 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33029 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33031 this.thumbEl.on('click', this.onClick, this);
33033 this.prevIndicator.on('click', this.prev, this);
33035 this.nextIndicator.on('click', this.next, this);
33039 initial : function()
33041 if(this.files.length){
33042 this.indicator = 1;
33046 this.fireEvent('initial', this);
33049 update : function()
33051 this.imageEl.attr('src', this.files[this.indicator - 1]);
33053 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33055 this.prevIndicator.show();
33057 if(this.indicator == 1){
33058 this.prevIndicator.hide();
33061 this.nextIndicator.show();
33063 if(this.indicator == this.files.length){
33064 this.nextIndicator.hide();
33067 this.thumbEl.scrollTo('top');
33069 this.fireEvent('update', this);
33072 onClick : function(e)
33074 e.preventDefault();
33076 this.fireEvent('click', this);
33081 e.preventDefault();
33083 this.indicator = Math.max(1, this.indicator - 1);
33090 e.preventDefault();
33092 this.indicator = Math.min(this.files.length, this.indicator + 1);
33106 * @class Roo.bootstrap.RadioSet
33107 * @extends Roo.bootstrap.Input
33108 * Bootstrap RadioSet class
33109 * @cfg {String} indicatorpos (left|right) default left
33110 * @cfg {Boolean} inline (true|false) inline the element (default true)
33111 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33113 * Create a new RadioSet
33114 * @param {Object} config The config object
33117 Roo.bootstrap.RadioSet = function(config){
33119 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33123 Roo.bootstrap.RadioSet.register(this);
33128 * Fires when the element is checked or unchecked.
33129 * @param {Roo.bootstrap.RadioSet} this This radio
33130 * @param {Roo.bootstrap.Radio} item The checked item
33137 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
33145 indicatorpos : 'left',
33147 getAutoCreate : function()
33151 cls : 'roo-radio-set-label',
33155 html : this.fieldLabel
33160 if(this.indicatorpos == 'left'){
33163 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33164 tooltip : 'This field is required'
33169 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33170 tooltip : 'This field is required'
33176 cls : 'roo-radio-set-items'
33179 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33181 if (align === 'left' && this.fieldLabel.length) {
33184 cls : "roo-radio-set-right",
33190 if(this.labelWidth > 12){
33191 label.style = "width: " + this.labelWidth + 'px';
33194 if(this.labelWidth < 13 && this.labelmd == 0){
33195 this.labelmd = this.labelWidth;
33198 if(this.labellg > 0){
33199 label.cls += ' col-lg-' + this.labellg;
33200 items.cls += ' col-lg-' + (12 - this.labellg);
33203 if(this.labelmd > 0){
33204 label.cls += ' col-md-' + this.labelmd;
33205 items.cls += ' col-md-' + (12 - this.labelmd);
33208 if(this.labelsm > 0){
33209 label.cls += ' col-sm-' + this.labelsm;
33210 items.cls += ' col-sm-' + (12 - this.labelsm);
33213 if(this.labelxs > 0){
33214 label.cls += ' col-xs-' + this.labelxs;
33215 items.cls += ' col-xs-' + (12 - this.labelxs);
33221 cls : 'roo-radio-set',
33225 cls : 'roo-radio-set-input',
33228 value : this.value ? this.value : ''
33235 if(this.weight.length){
33236 cfg.cls += ' roo-radio-' + this.weight;
33240 cfg.cls += ' roo-radio-set-inline';
33247 initEvents : function()
33249 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33250 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33252 if(!this.fieldLabel.length){
33253 this.labelEl.hide();
33256 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33257 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33259 this.indicatorEl().setVisibilityMode(Roo.Element.DISPLAY);
33260 this.indicatorEl().hide();
33262 this.originalValue = this.getValue();
33266 inputEl: function ()
33268 return this.el.select('.roo-radio-set-input', true).first();
33271 getChildContainer : function()
33273 return this.itemsEl;
33276 register : function(item)
33278 this.radioes.push(item);
33282 validate : function()
33286 Roo.each(this.radioes, function(i){
33295 if(this.allowBlank) {
33299 if(this.disabled || valid){
33304 this.markInvalid();
33309 markValid : function()
33311 if(this.labelEl.isVisible(true)){
33312 this.indicatorEl().hide();
33315 this.el.removeClass([this.invalidClass, this.validClass]);
33316 this.el.addClass(this.validClass);
33318 this.fireEvent('valid', this);
33321 markInvalid : function(msg)
33323 if(this.allowBlank || this.disabled){
33327 if(this.labelEl.isVisible(true)){
33328 this.indicatorEl().show();
33331 this.el.removeClass([this.invalidClass, this.validClass]);
33332 this.el.addClass(this.invalidClass);
33334 this.fireEvent('invalid', this, msg);
33338 setValue : function(v, suppressEvent)
33342 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33345 Roo.each(this.radioes, function(i){
33348 i.el.removeClass('checked');
33350 if(i.value === v || i.value.toString() === v.toString()){
33352 i.el.addClass('checked');
33354 if(suppressEvent !== true){
33355 this.fireEvent('check', this, i);
33364 clearInvalid : function(){
33366 if(!this.el || this.preventMark){
33370 this.el.removeClass([this.invalidClass]);
33372 this.fireEvent('valid', this);
33377 Roo.apply(Roo.bootstrap.RadioSet, {
33381 register : function(set)
33383 this.groups[set.name] = set;
33386 get: function(name)
33388 if (typeof(this.groups[name]) == 'undefined') {
33392 return this.groups[name] ;
33398 * Ext JS Library 1.1.1
33399 * Copyright(c) 2006-2007, Ext JS, LLC.
33401 * Originally Released Under LGPL - original licence link has changed is not relivant.
33404 * <script type="text/javascript">
33409 * @class Roo.bootstrap.SplitBar
33410 * @extends Roo.util.Observable
33411 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
33415 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
33416 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
33417 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
33418 split.minSize = 100;
33419 split.maxSize = 600;
33420 split.animate = true;
33421 split.on('moved', splitterMoved);
33424 * Create a new SplitBar
33425 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
33426 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
33427 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33428 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
33429 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
33430 position of the SplitBar).
33432 Roo.bootstrap.SplitBar = function(cfg){
33437 // dragElement : elm
33438 // resizingElement: el,
33440 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
33441 // placement : Roo.bootstrap.SplitBar.LEFT ,
33442 // existingProxy ???
33445 this.el = Roo.get(cfg.dragElement, true);
33446 this.el.dom.unselectable = "on";
33448 this.resizingEl = Roo.get(cfg.resizingElement, true);
33452 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33453 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
33456 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
33459 * The minimum size of the resizing element. (Defaults to 0)
33465 * The maximum size of the resizing element. (Defaults to 2000)
33468 this.maxSize = 2000;
33471 * Whether to animate the transition to the new size
33474 this.animate = false;
33477 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
33480 this.useShim = false;
33485 if(!cfg.existingProxy){
33487 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
33489 this.proxy = Roo.get(cfg.existingProxy).dom;
33492 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
33495 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
33498 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
33501 this.dragSpecs = {};
33504 * @private The adapter to use to positon and resize elements
33506 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33507 this.adapter.init(this);
33509 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33511 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
33512 this.el.addClass("roo-splitbar-h");
33515 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
33516 this.el.addClass("roo-splitbar-v");
33522 * Fires when the splitter is moved (alias for {@link #event-moved})
33523 * @param {Roo.bootstrap.SplitBar} this
33524 * @param {Number} newSize the new width or height
33529 * Fires when the splitter is moved
33530 * @param {Roo.bootstrap.SplitBar} this
33531 * @param {Number} newSize the new width or height
33535 * @event beforeresize
33536 * Fires before the splitter is dragged
33537 * @param {Roo.bootstrap.SplitBar} this
33539 "beforeresize" : true,
33541 "beforeapply" : true
33544 Roo.util.Observable.call(this);
33547 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
33548 onStartProxyDrag : function(x, y){
33549 this.fireEvent("beforeresize", this);
33551 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
33553 o.enableDisplayMode("block");
33554 // all splitbars share the same overlay
33555 Roo.bootstrap.SplitBar.prototype.overlay = o;
33557 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33558 this.overlay.show();
33559 Roo.get(this.proxy).setDisplayed("block");
33560 var size = this.adapter.getElementSize(this);
33561 this.activeMinSize = this.getMinimumSize();;
33562 this.activeMaxSize = this.getMaximumSize();;
33563 var c1 = size - this.activeMinSize;
33564 var c2 = Math.max(this.activeMaxSize - size, 0);
33565 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33566 this.dd.resetConstraints();
33567 this.dd.setXConstraint(
33568 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
33569 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
33571 this.dd.setYConstraint(0, 0);
33573 this.dd.resetConstraints();
33574 this.dd.setXConstraint(0, 0);
33575 this.dd.setYConstraint(
33576 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
33577 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
33580 this.dragSpecs.startSize = size;
33581 this.dragSpecs.startPoint = [x, y];
33582 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
33586 * @private Called after the drag operation by the DDProxy
33588 onEndProxyDrag : function(e){
33589 Roo.get(this.proxy).setDisplayed(false);
33590 var endPoint = Roo.lib.Event.getXY(e);
33592 this.overlay.hide();
33595 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33596 newSize = this.dragSpecs.startSize +
33597 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
33598 endPoint[0] - this.dragSpecs.startPoint[0] :
33599 this.dragSpecs.startPoint[0] - endPoint[0]
33602 newSize = this.dragSpecs.startSize +
33603 (this.placement == Roo.bootstrap.SplitBar.TOP ?
33604 endPoint[1] - this.dragSpecs.startPoint[1] :
33605 this.dragSpecs.startPoint[1] - endPoint[1]
33608 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
33609 if(newSize != this.dragSpecs.startSize){
33610 if(this.fireEvent('beforeapply', this, newSize) !== false){
33611 this.adapter.setElementSize(this, newSize);
33612 this.fireEvent("moved", this, newSize);
33613 this.fireEvent("resize", this, newSize);
33619 * Get the adapter this SplitBar uses
33620 * @return The adapter object
33622 getAdapter : function(){
33623 return this.adapter;
33627 * Set the adapter this SplitBar uses
33628 * @param {Object} adapter A SplitBar adapter object
33630 setAdapter : function(adapter){
33631 this.adapter = adapter;
33632 this.adapter.init(this);
33636 * Gets the minimum size for the resizing element
33637 * @return {Number} The minimum size
33639 getMinimumSize : function(){
33640 return this.minSize;
33644 * Sets the minimum size for the resizing element
33645 * @param {Number} minSize The minimum size
33647 setMinimumSize : function(minSize){
33648 this.minSize = minSize;
33652 * Gets the maximum size for the resizing element
33653 * @return {Number} The maximum size
33655 getMaximumSize : function(){
33656 return this.maxSize;
33660 * Sets the maximum size for the resizing element
33661 * @param {Number} maxSize The maximum size
33663 setMaximumSize : function(maxSize){
33664 this.maxSize = maxSize;
33668 * Sets the initialize size for the resizing element
33669 * @param {Number} size The initial size
33671 setCurrentSize : function(size){
33672 var oldAnimate = this.animate;
33673 this.animate = false;
33674 this.adapter.setElementSize(this, size);
33675 this.animate = oldAnimate;
33679 * Destroy this splitbar.
33680 * @param {Boolean} removeEl True to remove the element
33682 destroy : function(removeEl){
33684 this.shim.remove();
33687 this.proxy.parentNode.removeChild(this.proxy);
33695 * @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.
33697 Roo.bootstrap.SplitBar.createProxy = function(dir){
33698 var proxy = new Roo.Element(document.createElement("div"));
33699 proxy.unselectable();
33700 var cls = 'roo-splitbar-proxy';
33701 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
33702 document.body.appendChild(proxy.dom);
33707 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
33708 * Default Adapter. It assumes the splitter and resizing element are not positioned
33709 * elements and only gets/sets the width of the element. Generally used for table based layouts.
33711 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
33714 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
33715 // do nothing for now
33716 init : function(s){
33720 * Called before drag operations to get the current size of the resizing element.
33721 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33723 getElementSize : function(s){
33724 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33725 return s.resizingEl.getWidth();
33727 return s.resizingEl.getHeight();
33732 * Called after drag operations to set the size of the resizing element.
33733 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33734 * @param {Number} newSize The new size to set
33735 * @param {Function} onComplete A function to be invoked when resizing is complete
33737 setElementSize : function(s, newSize, onComplete){
33738 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33740 s.resizingEl.setWidth(newSize);
33742 onComplete(s, newSize);
33745 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
33750 s.resizingEl.setHeight(newSize);
33752 onComplete(s, newSize);
33755 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
33762 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
33763 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
33764 * Adapter that moves the splitter element to align with the resized sizing element.
33765 * Used with an absolute positioned SplitBar.
33766 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
33767 * document.body, make sure you assign an id to the body element.
33769 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
33770 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33771 this.container = Roo.get(container);
33774 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
33775 init : function(s){
33776 this.basic.init(s);
33779 getElementSize : function(s){
33780 return this.basic.getElementSize(s);
33783 setElementSize : function(s, newSize, onComplete){
33784 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
33787 moveSplitter : function(s){
33788 var yes = Roo.bootstrap.SplitBar;
33789 switch(s.placement){
33791 s.el.setX(s.resizingEl.getRight());
33794 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
33797 s.el.setY(s.resizingEl.getBottom());
33800 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
33807 * Orientation constant - Create a vertical SplitBar
33811 Roo.bootstrap.SplitBar.VERTICAL = 1;
33814 * Orientation constant - Create a horizontal SplitBar
33818 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
33821 * Placement constant - The resizing element is to the left of the splitter element
33825 Roo.bootstrap.SplitBar.LEFT = 1;
33828 * Placement constant - The resizing element is to the right of the splitter element
33832 Roo.bootstrap.SplitBar.RIGHT = 2;
33835 * Placement constant - The resizing element is positioned above the splitter element
33839 Roo.bootstrap.SplitBar.TOP = 3;
33842 * Placement constant - The resizing element is positioned under splitter element
33846 Roo.bootstrap.SplitBar.BOTTOM = 4;
33847 Roo.namespace("Roo.bootstrap.layout");/*
33849 * Ext JS Library 1.1.1
33850 * Copyright(c) 2006-2007, Ext JS, LLC.
33852 * Originally Released Under LGPL - original licence link has changed is not relivant.
33855 * <script type="text/javascript">
33859 * @class Roo.bootstrap.layout.Manager
33860 * @extends Roo.bootstrap.Component
33861 * Base class for layout managers.
33863 Roo.bootstrap.layout.Manager = function(config)
33865 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
33871 /** false to disable window resize monitoring @type Boolean */
33872 this.monitorWindowResize = true;
33877 * Fires when a layout is performed.
33878 * @param {Roo.LayoutManager} this
33882 * @event regionresized
33883 * Fires when the user resizes a region.
33884 * @param {Roo.LayoutRegion} region The resized region
33885 * @param {Number} newSize The new size (width for east/west, height for north/south)
33887 "regionresized" : true,
33889 * @event regioncollapsed
33890 * Fires when a region is collapsed.
33891 * @param {Roo.LayoutRegion} region The collapsed region
33893 "regioncollapsed" : true,
33895 * @event regionexpanded
33896 * Fires when a region is expanded.
33897 * @param {Roo.LayoutRegion} region The expanded region
33899 "regionexpanded" : true
33901 this.updating = false;
33904 this.el = Roo.get(config.el);
33910 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
33915 monitorWindowResize : true,
33921 onRender : function(ct, position)
33924 this.el = Roo.get(ct);
33927 //this.fireEvent('render',this);
33931 initEvents: function()
33935 // ie scrollbar fix
33936 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
33937 document.body.scroll = "no";
33938 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
33939 this.el.position('relative');
33941 this.id = this.el.id;
33942 this.el.addClass("roo-layout-container");
33943 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
33944 if(this.el.dom != document.body ) {
33945 this.el.on('resize', this.layout,this);
33946 this.el.on('show', this.layout,this);
33952 * Returns true if this layout is currently being updated
33953 * @return {Boolean}
33955 isUpdating : function(){
33956 return this.updating;
33960 * Suspend the LayoutManager from doing auto-layouts while
33961 * making multiple add or remove calls
33963 beginUpdate : function(){
33964 this.updating = true;
33968 * Restore auto-layouts and optionally disable the manager from performing a layout
33969 * @param {Boolean} noLayout true to disable a layout update
33971 endUpdate : function(noLayout){
33972 this.updating = false;
33978 layout: function(){
33982 onRegionResized : function(region, newSize){
33983 this.fireEvent("regionresized", region, newSize);
33987 onRegionCollapsed : function(region){
33988 this.fireEvent("regioncollapsed", region);
33991 onRegionExpanded : function(region){
33992 this.fireEvent("regionexpanded", region);
33996 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
33997 * performs box-model adjustments.
33998 * @return {Object} The size as an object {width: (the width), height: (the height)}
34000 getViewSize : function()
34003 if(this.el.dom != document.body){
34004 size = this.el.getSize();
34006 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34008 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34009 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34014 * Returns the Element this layout is bound to.
34015 * @return {Roo.Element}
34017 getEl : function(){
34022 * Returns the specified region.
34023 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34024 * @return {Roo.LayoutRegion}
34026 getRegion : function(target){
34027 return this.regions[target.toLowerCase()];
34030 onWindowResize : function(){
34031 if(this.monitorWindowResize){
34038 * Ext JS Library 1.1.1
34039 * Copyright(c) 2006-2007, Ext JS, LLC.
34041 * Originally Released Under LGPL - original licence link has changed is not relivant.
34044 * <script type="text/javascript">
34047 * @class Roo.bootstrap.layout.Border
34048 * @extends Roo.bootstrap.layout.Manager
34049 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34050 * please see: examples/bootstrap/nested.html<br><br>
34052 <b>The container the layout is rendered into can be either the body element or any other element.
34053 If it is not the body element, the container needs to either be an absolute positioned element,
34054 or you will need to add "position:relative" to the css of the container. You will also need to specify
34055 the container size if it is not the body element.</b>
34058 * Create a new Border
34059 * @param {Object} config Configuration options
34061 Roo.bootstrap.layout.Border = function(config){
34062 config = config || {};
34063 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34067 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34068 if(config[region]){
34069 config[region].region = region;
34070 this.addRegion(config[region]);
34076 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
34078 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34080 * Creates and adds a new region if it doesn't already exist.
34081 * @param {String} target The target region key (north, south, east, west or center).
34082 * @param {Object} config The regions config object
34083 * @return {BorderLayoutRegion} The new region
34085 addRegion : function(config)
34087 if(!this.regions[config.region]){
34088 var r = this.factory(config);
34089 this.bindRegion(r);
34091 return this.regions[config.region];
34095 bindRegion : function(r){
34096 this.regions[r.config.region] = r;
34098 r.on("visibilitychange", this.layout, this);
34099 r.on("paneladded", this.layout, this);
34100 r.on("panelremoved", this.layout, this);
34101 r.on("invalidated", this.layout, this);
34102 r.on("resized", this.onRegionResized, this);
34103 r.on("collapsed", this.onRegionCollapsed, this);
34104 r.on("expanded", this.onRegionExpanded, this);
34108 * Performs a layout update.
34110 layout : function()
34112 if(this.updating) {
34116 // render all the rebions if they have not been done alreayd?
34117 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34118 if(this.regions[region] && !this.regions[region].bodyEl){
34119 this.regions[region].onRender(this.el)
34123 var size = this.getViewSize();
34124 var w = size.width;
34125 var h = size.height;
34130 //var x = 0, y = 0;
34132 var rs = this.regions;
34133 var north = rs["north"];
34134 var south = rs["south"];
34135 var west = rs["west"];
34136 var east = rs["east"];
34137 var center = rs["center"];
34138 //if(this.hideOnLayout){ // not supported anymore
34139 //c.el.setStyle("display", "none");
34141 if(north && north.isVisible()){
34142 var b = north.getBox();
34143 var m = north.getMargins();
34144 b.width = w - (m.left+m.right);
34147 centerY = b.height + b.y + m.bottom;
34148 centerH -= centerY;
34149 north.updateBox(this.safeBox(b));
34151 if(south && south.isVisible()){
34152 var b = south.getBox();
34153 var m = south.getMargins();
34154 b.width = w - (m.left+m.right);
34156 var totalHeight = (b.height + m.top + m.bottom);
34157 b.y = h - totalHeight + m.top;
34158 centerH -= totalHeight;
34159 south.updateBox(this.safeBox(b));
34161 if(west && west.isVisible()){
34162 var b = west.getBox();
34163 var m = west.getMargins();
34164 b.height = centerH - (m.top+m.bottom);
34166 b.y = centerY + m.top;
34167 var totalWidth = (b.width + m.left + m.right);
34168 centerX += totalWidth;
34169 centerW -= totalWidth;
34170 west.updateBox(this.safeBox(b));
34172 if(east && east.isVisible()){
34173 var b = east.getBox();
34174 var m = east.getMargins();
34175 b.height = centerH - (m.top+m.bottom);
34176 var totalWidth = (b.width + m.left + m.right);
34177 b.x = w - totalWidth + m.left;
34178 b.y = centerY + m.top;
34179 centerW -= totalWidth;
34180 east.updateBox(this.safeBox(b));
34183 var m = center.getMargins();
34185 x: centerX + m.left,
34186 y: centerY + m.top,
34187 width: centerW - (m.left+m.right),
34188 height: centerH - (m.top+m.bottom)
34190 //if(this.hideOnLayout){
34191 //center.el.setStyle("display", "block");
34193 center.updateBox(this.safeBox(centerBox));
34196 this.fireEvent("layout", this);
34200 safeBox : function(box){
34201 box.width = Math.max(0, box.width);
34202 box.height = Math.max(0, box.height);
34207 * Adds a ContentPanel (or subclass) to this layout.
34208 * @param {String} target The target region key (north, south, east, west or center).
34209 * @param {Roo.ContentPanel} panel The panel to add
34210 * @return {Roo.ContentPanel} The added panel
34212 add : function(target, panel){
34214 target = target.toLowerCase();
34215 return this.regions[target].add(panel);
34219 * Remove a ContentPanel (or subclass) to this layout.
34220 * @param {String} target The target region key (north, south, east, west or center).
34221 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34222 * @return {Roo.ContentPanel} The removed panel
34224 remove : function(target, panel){
34225 target = target.toLowerCase();
34226 return this.regions[target].remove(panel);
34230 * Searches all regions for a panel with the specified id
34231 * @param {String} panelId
34232 * @return {Roo.ContentPanel} The panel or null if it wasn't found
34234 findPanel : function(panelId){
34235 var rs = this.regions;
34236 for(var target in rs){
34237 if(typeof rs[target] != "function"){
34238 var p = rs[target].getPanel(panelId);
34248 * Searches all regions for a panel with the specified id and activates (shows) it.
34249 * @param {String/ContentPanel} panelId The panels id or the panel itself
34250 * @return {Roo.ContentPanel} The shown panel or null
34252 showPanel : function(panelId) {
34253 var rs = this.regions;
34254 for(var target in rs){
34255 var r = rs[target];
34256 if(typeof r != "function"){
34257 if(r.hasPanel(panelId)){
34258 return r.showPanel(panelId);
34266 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34267 * @param {Roo.state.Provider} provider (optional) An alternate state provider
34270 restoreState : function(provider){
34272 provider = Roo.state.Manager;
34274 var sm = new Roo.LayoutStateManager();
34275 sm.init(this, provider);
34281 * Adds a xtype elements to the layout.
34285 xtype : 'ContentPanel',
34292 xtype : 'NestedLayoutPanel',
34298 items : [ ... list of content panels or nested layout panels.. ]
34302 * @param {Object} cfg Xtype definition of item to add.
34304 addxtype : function(cfg)
34306 // basically accepts a pannel...
34307 // can accept a layout region..!?!?
34308 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34311 // theory? children can only be panels??
34313 //if (!cfg.xtype.match(/Panel$/)) {
34318 if (typeof(cfg.region) == 'undefined') {
34319 Roo.log("Failed to add Panel, region was not set");
34323 var region = cfg.region;
34329 xitems = cfg.items;
34336 case 'Content': // ContentPanel (el, cfg)
34337 case 'Scroll': // ContentPanel (el, cfg)
34339 cfg.autoCreate = true;
34340 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34342 // var el = this.el.createChild();
34343 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34346 this.add(region, ret);
34350 case 'TreePanel': // our new panel!
34351 cfg.el = this.el.createChild();
34352 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34353 this.add(region, ret);
34358 // create a new Layout (which is a Border Layout...
34360 var clayout = cfg.layout;
34361 clayout.el = this.el.createChild();
34362 clayout.items = clayout.items || [];
34366 // replace this exitems with the clayout ones..
34367 xitems = clayout.items;
34369 // force background off if it's in center...
34370 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34371 cfg.background = false;
34373 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
34376 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34377 //console.log('adding nested layout panel ' + cfg.toSource());
34378 this.add(region, ret);
34379 nb = {}; /// find first...
34384 // needs grid and region
34386 //var el = this.getRegion(region).el.createChild();
34388 *var el = this.el.createChild();
34389 // create the grid first...
34390 cfg.grid.container = el;
34391 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
34394 if (region == 'center' && this.active ) {
34395 cfg.background = false;
34398 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34400 this.add(region, ret);
34402 if (cfg.background) {
34403 // render grid on panel activation (if panel background)
34404 ret.on('activate', function(gp) {
34405 if (!gp.grid.rendered) {
34406 // gp.grid.render(el);
34410 // cfg.grid.render(el);
34416 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
34417 // it was the old xcomponent building that caused this before.
34418 // espeically if border is the top element in the tree.
34428 if (typeof(Roo[cfg.xtype]) != 'undefined') {
34430 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34431 this.add(region, ret);
34435 throw "Can not add '" + cfg.xtype + "' to Border";
34441 this.beginUpdate();
34445 Roo.each(xitems, function(i) {
34446 region = nb && i.region ? i.region : false;
34448 var add = ret.addxtype(i);
34451 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
34452 if (!i.background) {
34453 abn[region] = nb[region] ;
34460 // make the last non-background panel active..
34461 //if (nb) { Roo.log(abn); }
34464 for(var r in abn) {
34465 region = this.getRegion(r);
34467 // tried using nb[r], but it does not work..
34469 region.showPanel(abn[r]);
34480 factory : function(cfg)
34483 var validRegions = Roo.bootstrap.layout.Border.regions;
34485 var target = cfg.region;
34488 var r = Roo.bootstrap.layout;
34492 return new r.North(cfg);
34494 return new r.South(cfg);
34496 return new r.East(cfg);
34498 return new r.West(cfg);
34500 return new r.Center(cfg);
34502 throw 'Layout region "'+target+'" not supported.';
34509 * Ext JS Library 1.1.1
34510 * Copyright(c) 2006-2007, Ext JS, LLC.
34512 * Originally Released Under LGPL - original licence link has changed is not relivant.
34515 * <script type="text/javascript">
34519 * @class Roo.bootstrap.layout.Basic
34520 * @extends Roo.util.Observable
34521 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
34522 * and does not have a titlebar, tabs or any other features. All it does is size and position
34523 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
34524 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
34525 * @cfg {string} region the region that it inhabits..
34526 * @cfg {bool} skipConfig skip config?
34530 Roo.bootstrap.layout.Basic = function(config){
34532 this.mgr = config.mgr;
34534 this.position = config.region;
34536 var skipConfig = config.skipConfig;
34540 * @scope Roo.BasicLayoutRegion
34544 * @event beforeremove
34545 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
34546 * @param {Roo.LayoutRegion} this
34547 * @param {Roo.ContentPanel} panel The panel
34548 * @param {Object} e The cancel event object
34550 "beforeremove" : true,
34552 * @event invalidated
34553 * Fires when the layout for this region is changed.
34554 * @param {Roo.LayoutRegion} this
34556 "invalidated" : true,
34558 * @event visibilitychange
34559 * Fires when this region is shown or hidden
34560 * @param {Roo.LayoutRegion} this
34561 * @param {Boolean} visibility true or false
34563 "visibilitychange" : true,
34565 * @event paneladded
34566 * Fires when a panel is added.
34567 * @param {Roo.LayoutRegion} this
34568 * @param {Roo.ContentPanel} panel The panel
34570 "paneladded" : true,
34572 * @event panelremoved
34573 * Fires when a panel is removed.
34574 * @param {Roo.LayoutRegion} this
34575 * @param {Roo.ContentPanel} panel The panel
34577 "panelremoved" : true,
34579 * @event beforecollapse
34580 * Fires when this region before collapse.
34581 * @param {Roo.LayoutRegion} this
34583 "beforecollapse" : true,
34586 * Fires when this region is collapsed.
34587 * @param {Roo.LayoutRegion} this
34589 "collapsed" : true,
34592 * Fires when this region is expanded.
34593 * @param {Roo.LayoutRegion} this
34598 * Fires when this region is slid into view.
34599 * @param {Roo.LayoutRegion} this
34601 "slideshow" : true,
34604 * Fires when this region slides out of view.
34605 * @param {Roo.LayoutRegion} this
34607 "slidehide" : true,
34609 * @event panelactivated
34610 * Fires when a panel is activated.
34611 * @param {Roo.LayoutRegion} this
34612 * @param {Roo.ContentPanel} panel The activated panel
34614 "panelactivated" : true,
34617 * Fires when the user resizes this region.
34618 * @param {Roo.LayoutRegion} this
34619 * @param {Number} newSize The new size (width for east/west, height for north/south)
34623 /** A collection of panels in this region. @type Roo.util.MixedCollection */
34624 this.panels = new Roo.util.MixedCollection();
34625 this.panels.getKey = this.getPanelId.createDelegate(this);
34627 this.activePanel = null;
34628 // ensure listeners are added...
34630 if (config.listeners || config.events) {
34631 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
34632 listeners : config.listeners || {},
34633 events : config.events || {}
34637 if(skipConfig !== true){
34638 this.applyConfig(config);
34642 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
34644 getPanelId : function(p){
34648 applyConfig : function(config){
34649 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
34650 this.config = config;
34655 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
34656 * the width, for horizontal (north, south) the height.
34657 * @param {Number} newSize The new width or height
34659 resizeTo : function(newSize){
34660 var el = this.el ? this.el :
34661 (this.activePanel ? this.activePanel.getEl() : null);
34663 switch(this.position){
34666 el.setWidth(newSize);
34667 this.fireEvent("resized", this, newSize);
34671 el.setHeight(newSize);
34672 this.fireEvent("resized", this, newSize);
34678 getBox : function(){
34679 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
34682 getMargins : function(){
34683 return this.margins;
34686 updateBox : function(box){
34688 var el = this.activePanel.getEl();
34689 el.dom.style.left = box.x + "px";
34690 el.dom.style.top = box.y + "px";
34691 this.activePanel.setSize(box.width, box.height);
34695 * Returns the container element for this region.
34696 * @return {Roo.Element}
34698 getEl : function(){
34699 return this.activePanel;
34703 * Returns true if this region is currently visible.
34704 * @return {Boolean}
34706 isVisible : function(){
34707 return this.activePanel ? true : false;
34710 setActivePanel : function(panel){
34711 panel = this.getPanel(panel);
34712 if(this.activePanel && this.activePanel != panel){
34713 this.activePanel.setActiveState(false);
34714 this.activePanel.getEl().setLeftTop(-10000,-10000);
34716 this.activePanel = panel;
34717 panel.setActiveState(true);
34719 panel.setSize(this.box.width, this.box.height);
34721 this.fireEvent("panelactivated", this, panel);
34722 this.fireEvent("invalidated");
34726 * Show the specified panel.
34727 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
34728 * @return {Roo.ContentPanel} The shown panel or null
34730 showPanel : function(panel){
34731 panel = this.getPanel(panel);
34733 this.setActivePanel(panel);
34739 * Get the active panel for this region.
34740 * @return {Roo.ContentPanel} The active panel or null
34742 getActivePanel : function(){
34743 return this.activePanel;
34747 * Add the passed ContentPanel(s)
34748 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34749 * @return {Roo.ContentPanel} The panel added (if only one was added)
34751 add : function(panel){
34752 if(arguments.length > 1){
34753 for(var i = 0, len = arguments.length; i < len; i++) {
34754 this.add(arguments[i]);
34758 if(this.hasPanel(panel)){
34759 this.showPanel(panel);
34762 var el = panel.getEl();
34763 if(el.dom.parentNode != this.mgr.el.dom){
34764 this.mgr.el.dom.appendChild(el.dom);
34766 if(panel.setRegion){
34767 panel.setRegion(this);
34769 this.panels.add(panel);
34770 el.setStyle("position", "absolute");
34771 if(!panel.background){
34772 this.setActivePanel(panel);
34773 if(this.config.initialSize && this.panels.getCount()==1){
34774 this.resizeTo(this.config.initialSize);
34777 this.fireEvent("paneladded", this, panel);
34782 * Returns true if the panel is in this region.
34783 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34784 * @return {Boolean}
34786 hasPanel : function(panel){
34787 if(typeof panel == "object"){ // must be panel obj
34788 panel = panel.getId();
34790 return this.getPanel(panel) ? true : false;
34794 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34795 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34796 * @param {Boolean} preservePanel Overrides the config preservePanel option
34797 * @return {Roo.ContentPanel} The panel that was removed
34799 remove : function(panel, preservePanel){
34800 panel = this.getPanel(panel);
34805 this.fireEvent("beforeremove", this, panel, e);
34806 if(e.cancel === true){
34809 var panelId = panel.getId();
34810 this.panels.removeKey(panelId);
34815 * Returns the panel specified or null if it's not in this region.
34816 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34817 * @return {Roo.ContentPanel}
34819 getPanel : function(id){
34820 if(typeof id == "object"){ // must be panel obj
34823 return this.panels.get(id);
34827 * Returns this regions position (north/south/east/west/center).
34830 getPosition: function(){
34831 return this.position;
34835 * Ext JS Library 1.1.1
34836 * Copyright(c) 2006-2007, Ext JS, LLC.
34838 * Originally Released Under LGPL - original licence link has changed is not relivant.
34841 * <script type="text/javascript">
34845 * @class Roo.bootstrap.layout.Region
34846 * @extends Roo.bootstrap.layout.Basic
34847 * This class represents a region in a layout manager.
34849 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
34850 * @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})
34851 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
34852 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
34853 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
34854 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
34855 * @cfg {String} title The title for the region (overrides panel titles)
34856 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
34857 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
34858 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
34859 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
34860 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
34861 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
34862 * the space available, similar to FireFox 1.5 tabs (defaults to false)
34863 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
34864 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
34865 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
34867 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
34868 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
34869 * @cfg {Boolean} disableTabTips True to disable tab tooltips
34870 * @cfg {Number} width For East/West panels
34871 * @cfg {Number} height For North/South panels
34872 * @cfg {Boolean} split To show the splitter
34873 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
34875 * @cfg {string} cls Extra CSS classes to add to region
34877 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
34878 * @cfg {string} region the region that it inhabits..
34881 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
34882 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
34884 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
34885 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
34886 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
34888 Roo.bootstrap.layout.Region = function(config)
34890 this.applyConfig(config);
34892 var mgr = config.mgr;
34893 var pos = config.region;
34894 config.skipConfig = true;
34895 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
34898 this.onRender(mgr.el);
34901 this.visible = true;
34902 this.collapsed = false;
34903 this.unrendered_panels = [];
34906 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
34908 position: '', // set by wrapper (eg. north/south etc..)
34909 unrendered_panels : null, // unrendered panels.
34910 createBody : function(){
34911 /** This region's body element
34912 * @type Roo.Element */
34913 this.bodyEl = this.el.createChild({
34915 cls: "roo-layout-panel-body tab-content" // bootstrap added...
34919 onRender: function(ctr, pos)
34921 var dh = Roo.DomHelper;
34922 /** This region's container element
34923 * @type Roo.Element */
34924 this.el = dh.append(ctr.dom, {
34926 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
34928 /** This region's title element
34929 * @type Roo.Element */
34931 this.titleEl = dh.append(this.el.dom,
34934 unselectable: "on",
34935 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
34937 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
34938 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
34941 this.titleEl.enableDisplayMode();
34942 /** This region's title text element
34943 * @type HTMLElement */
34944 this.titleTextEl = this.titleEl.dom.firstChild;
34945 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
34947 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
34948 this.closeBtn.enableDisplayMode();
34949 this.closeBtn.on("click", this.closeClicked, this);
34950 this.closeBtn.hide();
34952 this.createBody(this.config);
34953 if(this.config.hideWhenEmpty){
34955 this.on("paneladded", this.validateVisibility, this);
34956 this.on("panelremoved", this.validateVisibility, this);
34958 if(this.autoScroll){
34959 this.bodyEl.setStyle("overflow", "auto");
34961 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
34963 //if(c.titlebar !== false){
34964 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
34965 this.titleEl.hide();
34967 this.titleEl.show();
34968 if(this.config.title){
34969 this.titleTextEl.innerHTML = this.config.title;
34973 if(this.config.collapsed){
34974 this.collapse(true);
34976 if(this.config.hidden){
34980 if (this.unrendered_panels && this.unrendered_panels.length) {
34981 for (var i =0;i< this.unrendered_panels.length; i++) {
34982 this.add(this.unrendered_panels[i]);
34984 this.unrendered_panels = null;
34990 applyConfig : function(c)
34993 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
34994 var dh = Roo.DomHelper;
34995 if(c.titlebar !== false){
34996 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
34997 this.collapseBtn.on("click", this.collapse, this);
34998 this.collapseBtn.enableDisplayMode();
35000 if(c.showPin === true || this.showPin){
35001 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35002 this.stickBtn.enableDisplayMode();
35003 this.stickBtn.on("click", this.expand, this);
35004 this.stickBtn.hide();
35009 /** This region's collapsed element
35010 * @type Roo.Element */
35013 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35014 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35017 if(c.floatable !== false){
35018 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35019 this.collapsedEl.on("click", this.collapseClick, this);
35022 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35023 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35024 id: "message", unselectable: "on", style:{"float":"left"}});
35025 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35027 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35028 this.expandBtn.on("click", this.expand, this);
35032 if(this.collapseBtn){
35033 this.collapseBtn.setVisible(c.collapsible == true);
35036 this.cmargins = c.cmargins || this.cmargins ||
35037 (this.position == "west" || this.position == "east" ?
35038 {top: 0, left: 2, right:2, bottom: 0} :
35039 {top: 2, left: 0, right:0, bottom: 2});
35041 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35044 this.bottomTabs = c.tabPosition != "top";
35046 this.autoScroll = c.autoScroll || false;
35051 this.duration = c.duration || .30;
35052 this.slideDuration = c.slideDuration || .45;
35057 * Returns true if this region is currently visible.
35058 * @return {Boolean}
35060 isVisible : function(){
35061 return this.visible;
35065 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35066 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
35068 //setCollapsedTitle : function(title){
35069 // title = title || " ";
35070 // if(this.collapsedTitleTextEl){
35071 // this.collapsedTitleTextEl.innerHTML = title;
35075 getBox : function(){
35077 // if(!this.collapsed){
35078 b = this.el.getBox(false, true);
35080 // b = this.collapsedEl.getBox(false, true);
35085 getMargins : function(){
35086 return this.margins;
35087 //return this.collapsed ? this.cmargins : this.margins;
35090 highlight : function(){
35091 this.el.addClass("x-layout-panel-dragover");
35094 unhighlight : function(){
35095 this.el.removeClass("x-layout-panel-dragover");
35098 updateBox : function(box)
35100 if (!this.bodyEl) {
35101 return; // not rendered yet..
35105 if(!this.collapsed){
35106 this.el.dom.style.left = box.x + "px";
35107 this.el.dom.style.top = box.y + "px";
35108 this.updateBody(box.width, box.height);
35110 this.collapsedEl.dom.style.left = box.x + "px";
35111 this.collapsedEl.dom.style.top = box.y + "px";
35112 this.collapsedEl.setSize(box.width, box.height);
35115 this.tabs.autoSizeTabs();
35119 updateBody : function(w, h)
35122 this.el.setWidth(w);
35123 w -= this.el.getBorderWidth("rl");
35124 if(this.config.adjustments){
35125 w += this.config.adjustments[0];
35128 if(h !== null && h > 0){
35129 this.el.setHeight(h);
35130 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35131 h -= this.el.getBorderWidth("tb");
35132 if(this.config.adjustments){
35133 h += this.config.adjustments[1];
35135 this.bodyEl.setHeight(h);
35137 h = this.tabs.syncHeight(h);
35140 if(this.panelSize){
35141 w = w !== null ? w : this.panelSize.width;
35142 h = h !== null ? h : this.panelSize.height;
35144 if(this.activePanel){
35145 var el = this.activePanel.getEl();
35146 w = w !== null ? w : el.getWidth();
35147 h = h !== null ? h : el.getHeight();
35148 this.panelSize = {width: w, height: h};
35149 this.activePanel.setSize(w, h);
35151 if(Roo.isIE && this.tabs){
35152 this.tabs.el.repaint();
35157 * Returns the container element for this region.
35158 * @return {Roo.Element}
35160 getEl : function(){
35165 * Hides this region.
35168 //if(!this.collapsed){
35169 this.el.dom.style.left = "-2000px";
35172 // this.collapsedEl.dom.style.left = "-2000px";
35173 // this.collapsedEl.hide();
35175 this.visible = false;
35176 this.fireEvent("visibilitychange", this, false);
35180 * Shows this region if it was previously hidden.
35183 //if(!this.collapsed){
35186 // this.collapsedEl.show();
35188 this.visible = true;
35189 this.fireEvent("visibilitychange", this, true);
35192 closeClicked : function(){
35193 if(this.activePanel){
35194 this.remove(this.activePanel);
35198 collapseClick : function(e){
35200 e.stopPropagation();
35203 e.stopPropagation();
35209 * Collapses this region.
35210 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35213 collapse : function(skipAnim, skipCheck = false){
35214 if(this.collapsed) {
35218 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35220 this.collapsed = true;
35222 this.split.el.hide();
35224 if(this.config.animate && skipAnim !== true){
35225 this.fireEvent("invalidated", this);
35226 this.animateCollapse();
35228 this.el.setLocation(-20000,-20000);
35230 this.collapsedEl.show();
35231 this.fireEvent("collapsed", this);
35232 this.fireEvent("invalidated", this);
35238 animateCollapse : function(){
35243 * Expands this region if it was previously collapsed.
35244 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35245 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35248 expand : function(e, skipAnim){
35250 e.stopPropagation();
35252 if(!this.collapsed || this.el.hasActiveFx()) {
35256 this.afterSlideIn();
35259 this.collapsed = false;
35260 if(this.config.animate && skipAnim !== true){
35261 this.animateExpand();
35265 this.split.el.show();
35267 this.collapsedEl.setLocation(-2000,-2000);
35268 this.collapsedEl.hide();
35269 this.fireEvent("invalidated", this);
35270 this.fireEvent("expanded", this);
35274 animateExpand : function(){
35278 initTabs : function()
35280 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35282 var ts = new Roo.bootstrap.panel.Tabs({
35283 el: this.bodyEl.dom,
35284 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35285 disableTooltips: this.config.disableTabTips,
35286 toolbar : this.config.toolbar
35289 if(this.config.hideTabs){
35290 ts.stripWrap.setDisplayed(false);
35293 ts.resizeTabs = this.config.resizeTabs === true;
35294 ts.minTabWidth = this.config.minTabWidth || 40;
35295 ts.maxTabWidth = this.config.maxTabWidth || 250;
35296 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35297 ts.monitorResize = false;
35298 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35299 ts.bodyEl.addClass('roo-layout-tabs-body');
35300 this.panels.each(this.initPanelAsTab, this);
35303 initPanelAsTab : function(panel){
35304 var ti = this.tabs.addTab(
35308 this.config.closeOnTab && panel.isClosable(),
35311 if(panel.tabTip !== undefined){
35312 ti.setTooltip(panel.tabTip);
35314 ti.on("activate", function(){
35315 this.setActivePanel(panel);
35318 if(this.config.closeOnTab){
35319 ti.on("beforeclose", function(t, e){
35321 this.remove(panel);
35325 panel.tabItem = ti;
35330 updatePanelTitle : function(panel, title)
35332 if(this.activePanel == panel){
35333 this.updateTitle(title);
35336 var ti = this.tabs.getTab(panel.getEl().id);
35338 if(panel.tabTip !== undefined){
35339 ti.setTooltip(panel.tabTip);
35344 updateTitle : function(title){
35345 if(this.titleTextEl && !this.config.title){
35346 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
35350 setActivePanel : function(panel)
35352 panel = this.getPanel(panel);
35353 if(this.activePanel && this.activePanel != panel){
35354 this.activePanel.setActiveState(false);
35356 this.activePanel = panel;
35357 panel.setActiveState(true);
35358 if(this.panelSize){
35359 panel.setSize(this.panelSize.width, this.panelSize.height);
35362 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35364 this.updateTitle(panel.getTitle());
35366 this.fireEvent("invalidated", this);
35368 this.fireEvent("panelactivated", this, panel);
35372 * Shows the specified panel.
35373 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35374 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35376 showPanel : function(panel)
35378 panel = this.getPanel(panel);
35381 var tab = this.tabs.getTab(panel.getEl().id);
35382 if(tab.isHidden()){
35383 this.tabs.unhideTab(tab.id);
35387 this.setActivePanel(panel);
35394 * Get the active panel for this region.
35395 * @return {Roo.ContentPanel} The active panel or null
35397 getActivePanel : function(){
35398 return this.activePanel;
35401 validateVisibility : function(){
35402 if(this.panels.getCount() < 1){
35403 this.updateTitle(" ");
35404 this.closeBtn.hide();
35407 if(!this.isVisible()){
35414 * Adds the passed ContentPanel(s) to this region.
35415 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35416 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
35418 add : function(panel)
35420 if(arguments.length > 1){
35421 for(var i = 0, len = arguments.length; i < len; i++) {
35422 this.add(arguments[i]);
35427 // if we have not been rendered yet, then we can not really do much of this..
35428 if (!this.bodyEl) {
35429 this.unrendered_panels.push(panel);
35436 if(this.hasPanel(panel)){
35437 this.showPanel(panel);
35440 panel.setRegion(this);
35441 this.panels.add(panel);
35442 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
35443 // sinle panel - no tab...?? would it not be better to render it with the tabs,
35444 // and hide them... ???
35445 this.bodyEl.dom.appendChild(panel.getEl().dom);
35446 if(panel.background !== true){
35447 this.setActivePanel(panel);
35449 this.fireEvent("paneladded", this, panel);
35456 this.initPanelAsTab(panel);
35460 if(panel.background !== true){
35461 this.tabs.activate(panel.getEl().id);
35463 this.fireEvent("paneladded", this, panel);
35468 * Hides the tab for the specified panel.
35469 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35471 hidePanel : function(panel){
35472 if(this.tabs && (panel = this.getPanel(panel))){
35473 this.tabs.hideTab(panel.getEl().id);
35478 * Unhides the tab for a previously hidden panel.
35479 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35481 unhidePanel : function(panel){
35482 if(this.tabs && (panel = this.getPanel(panel))){
35483 this.tabs.unhideTab(panel.getEl().id);
35487 clearPanels : function(){
35488 while(this.panels.getCount() > 0){
35489 this.remove(this.panels.first());
35494 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35495 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35496 * @param {Boolean} preservePanel Overrides the config preservePanel option
35497 * @return {Roo.ContentPanel} The panel that was removed
35499 remove : function(panel, preservePanel)
35501 panel = this.getPanel(panel);
35506 this.fireEvent("beforeremove", this, panel, e);
35507 if(e.cancel === true){
35510 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
35511 var panelId = panel.getId();
35512 this.panels.removeKey(panelId);
35514 document.body.appendChild(panel.getEl().dom);
35517 this.tabs.removeTab(panel.getEl().id);
35518 }else if (!preservePanel){
35519 this.bodyEl.dom.removeChild(panel.getEl().dom);
35521 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
35522 var p = this.panels.first();
35523 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
35524 tempEl.appendChild(p.getEl().dom);
35525 this.bodyEl.update("");
35526 this.bodyEl.dom.appendChild(p.getEl().dom);
35528 this.updateTitle(p.getTitle());
35530 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
35531 this.setActivePanel(p);
35533 panel.setRegion(null);
35534 if(this.activePanel == panel){
35535 this.activePanel = null;
35537 if(this.config.autoDestroy !== false && preservePanel !== true){
35538 try{panel.destroy();}catch(e){}
35540 this.fireEvent("panelremoved", this, panel);
35545 * Returns the TabPanel component used by this region
35546 * @return {Roo.TabPanel}
35548 getTabs : function(){
35552 createTool : function(parentEl, className){
35553 var btn = Roo.DomHelper.append(parentEl, {
35555 cls: "x-layout-tools-button",
35558 cls: "roo-layout-tools-button-inner " + className,
35562 btn.addClassOnOver("roo-layout-tools-button-over");
35567 * Ext JS Library 1.1.1
35568 * Copyright(c) 2006-2007, Ext JS, LLC.
35570 * Originally Released Under LGPL - original licence link has changed is not relivant.
35573 * <script type="text/javascript">
35579 * @class Roo.SplitLayoutRegion
35580 * @extends Roo.LayoutRegion
35581 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
35583 Roo.bootstrap.layout.Split = function(config){
35584 this.cursor = config.cursor;
35585 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
35588 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
35590 splitTip : "Drag to resize.",
35591 collapsibleSplitTip : "Drag to resize. Double click to hide.",
35592 useSplitTips : false,
35594 applyConfig : function(config){
35595 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
35598 onRender : function(ctr,pos) {
35600 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
35601 if(!this.config.split){
35606 var splitEl = Roo.DomHelper.append(ctr.dom, {
35608 id: this.el.id + "-split",
35609 cls: "roo-layout-split roo-layout-split-"+this.position,
35612 /** The SplitBar for this region
35613 * @type Roo.SplitBar */
35614 // does not exist yet...
35615 Roo.log([this.position, this.orientation]);
35617 this.split = new Roo.bootstrap.SplitBar({
35618 dragElement : splitEl,
35619 resizingElement: this.el,
35620 orientation : this.orientation
35623 this.split.on("moved", this.onSplitMove, this);
35624 this.split.useShim = this.config.useShim === true;
35625 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
35626 if(this.useSplitTips){
35627 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
35629 //if(config.collapsible){
35630 // this.split.el.on("dblclick", this.collapse, this);
35633 if(typeof this.config.minSize != "undefined"){
35634 this.split.minSize = this.config.minSize;
35636 if(typeof this.config.maxSize != "undefined"){
35637 this.split.maxSize = this.config.maxSize;
35639 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
35640 this.hideSplitter();
35645 getHMaxSize : function(){
35646 var cmax = this.config.maxSize || 10000;
35647 var center = this.mgr.getRegion("center");
35648 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
35651 getVMaxSize : function(){
35652 var cmax = this.config.maxSize || 10000;
35653 var center = this.mgr.getRegion("center");
35654 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
35657 onSplitMove : function(split, newSize){
35658 this.fireEvent("resized", this, newSize);
35662 * Returns the {@link Roo.SplitBar} for this region.
35663 * @return {Roo.SplitBar}
35665 getSplitBar : function(){
35670 this.hideSplitter();
35671 Roo.bootstrap.layout.Split.superclass.hide.call(this);
35674 hideSplitter : function(){
35676 this.split.el.setLocation(-2000,-2000);
35677 this.split.el.hide();
35683 this.split.el.show();
35685 Roo.bootstrap.layout.Split.superclass.show.call(this);
35688 beforeSlide: function(){
35689 if(Roo.isGecko){// firefox overflow auto bug workaround
35690 this.bodyEl.clip();
35692 this.tabs.bodyEl.clip();
35694 if(this.activePanel){
35695 this.activePanel.getEl().clip();
35697 if(this.activePanel.beforeSlide){
35698 this.activePanel.beforeSlide();
35704 afterSlide : function(){
35705 if(Roo.isGecko){// firefox overflow auto bug workaround
35706 this.bodyEl.unclip();
35708 this.tabs.bodyEl.unclip();
35710 if(this.activePanel){
35711 this.activePanel.getEl().unclip();
35712 if(this.activePanel.afterSlide){
35713 this.activePanel.afterSlide();
35719 initAutoHide : function(){
35720 if(this.autoHide !== false){
35721 if(!this.autoHideHd){
35722 var st = new Roo.util.DelayedTask(this.slideIn, this);
35723 this.autoHideHd = {
35724 "mouseout": function(e){
35725 if(!e.within(this.el, true)){
35729 "mouseover" : function(e){
35735 this.el.on(this.autoHideHd);
35739 clearAutoHide : function(){
35740 if(this.autoHide !== false){
35741 this.el.un("mouseout", this.autoHideHd.mouseout);
35742 this.el.un("mouseover", this.autoHideHd.mouseover);
35746 clearMonitor : function(){
35747 Roo.get(document).un("click", this.slideInIf, this);
35750 // these names are backwards but not changed for compat
35751 slideOut : function(){
35752 if(this.isSlid || this.el.hasActiveFx()){
35755 this.isSlid = true;
35756 if(this.collapseBtn){
35757 this.collapseBtn.hide();
35759 this.closeBtnState = this.closeBtn.getStyle('display');
35760 this.closeBtn.hide();
35762 this.stickBtn.show();
35765 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
35766 this.beforeSlide();
35767 this.el.setStyle("z-index", 10001);
35768 this.el.slideIn(this.getSlideAnchor(), {
35769 callback: function(){
35771 this.initAutoHide();
35772 Roo.get(document).on("click", this.slideInIf, this);
35773 this.fireEvent("slideshow", this);
35780 afterSlideIn : function(){
35781 this.clearAutoHide();
35782 this.isSlid = false;
35783 this.clearMonitor();
35784 this.el.setStyle("z-index", "");
35785 if(this.collapseBtn){
35786 this.collapseBtn.show();
35788 this.closeBtn.setStyle('display', this.closeBtnState);
35790 this.stickBtn.hide();
35792 this.fireEvent("slidehide", this);
35795 slideIn : function(cb){
35796 if(!this.isSlid || this.el.hasActiveFx()){
35800 this.isSlid = false;
35801 this.beforeSlide();
35802 this.el.slideOut(this.getSlideAnchor(), {
35803 callback: function(){
35804 this.el.setLeftTop(-10000, -10000);
35806 this.afterSlideIn();
35814 slideInIf : function(e){
35815 if(!e.within(this.el)){
35820 animateCollapse : function(){
35821 this.beforeSlide();
35822 this.el.setStyle("z-index", 20000);
35823 var anchor = this.getSlideAnchor();
35824 this.el.slideOut(anchor, {
35825 callback : function(){
35826 this.el.setStyle("z-index", "");
35827 this.collapsedEl.slideIn(anchor, {duration:.3});
35829 this.el.setLocation(-10000,-10000);
35831 this.fireEvent("collapsed", this);
35838 animateExpand : function(){
35839 this.beforeSlide();
35840 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
35841 this.el.setStyle("z-index", 20000);
35842 this.collapsedEl.hide({
35845 this.el.slideIn(this.getSlideAnchor(), {
35846 callback : function(){
35847 this.el.setStyle("z-index", "");
35850 this.split.el.show();
35852 this.fireEvent("invalidated", this);
35853 this.fireEvent("expanded", this);
35881 getAnchor : function(){
35882 return this.anchors[this.position];
35885 getCollapseAnchor : function(){
35886 return this.canchors[this.position];
35889 getSlideAnchor : function(){
35890 return this.sanchors[this.position];
35893 getAlignAdj : function(){
35894 var cm = this.cmargins;
35895 switch(this.position){
35911 getExpandAdj : function(){
35912 var c = this.collapsedEl, cm = this.cmargins;
35913 switch(this.position){
35915 return [-(cm.right+c.getWidth()+cm.left), 0];
35918 return [cm.right+c.getWidth()+cm.left, 0];
35921 return [0, -(cm.top+cm.bottom+c.getHeight())];
35924 return [0, cm.top+cm.bottom+c.getHeight()];
35930 * Ext JS Library 1.1.1
35931 * Copyright(c) 2006-2007, Ext JS, LLC.
35933 * Originally Released Under LGPL - original licence link has changed is not relivant.
35936 * <script type="text/javascript">
35939 * These classes are private internal classes
35941 Roo.bootstrap.layout.Center = function(config){
35942 config.region = "center";
35943 Roo.bootstrap.layout.Region.call(this, config);
35944 this.visible = true;
35945 this.minWidth = config.minWidth || 20;
35946 this.minHeight = config.minHeight || 20;
35949 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
35951 // center panel can't be hidden
35955 // center panel can't be hidden
35958 getMinWidth: function(){
35959 return this.minWidth;
35962 getMinHeight: function(){
35963 return this.minHeight;
35976 Roo.bootstrap.layout.North = function(config)
35978 config.region = 'north';
35979 config.cursor = 'n-resize';
35981 Roo.bootstrap.layout.Split.call(this, config);
35985 this.split.placement = Roo.bootstrap.SplitBar.TOP;
35986 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
35987 this.split.el.addClass("roo-layout-split-v");
35989 var size = config.initialSize || config.height;
35990 if(typeof size != "undefined"){
35991 this.el.setHeight(size);
35994 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
35996 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36000 getBox : function(){
36001 if(this.collapsed){
36002 return this.collapsedEl.getBox();
36004 var box = this.el.getBox();
36006 box.height += this.split.el.getHeight();
36011 updateBox : function(box){
36012 if(this.split && !this.collapsed){
36013 box.height -= this.split.el.getHeight();
36014 this.split.el.setLeft(box.x);
36015 this.split.el.setTop(box.y+box.height);
36016 this.split.el.setWidth(box.width);
36018 if(this.collapsed){
36019 this.updateBody(box.width, null);
36021 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36029 Roo.bootstrap.layout.South = function(config){
36030 config.region = 'south';
36031 config.cursor = 's-resize';
36032 Roo.bootstrap.layout.Split.call(this, config);
36034 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36035 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36036 this.split.el.addClass("roo-layout-split-v");
36038 var size = config.initialSize || config.height;
36039 if(typeof size != "undefined"){
36040 this.el.setHeight(size);
36044 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36045 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36046 getBox : function(){
36047 if(this.collapsed){
36048 return this.collapsedEl.getBox();
36050 var box = this.el.getBox();
36052 var sh = this.split.el.getHeight();
36059 updateBox : function(box){
36060 if(this.split && !this.collapsed){
36061 var sh = this.split.el.getHeight();
36064 this.split.el.setLeft(box.x);
36065 this.split.el.setTop(box.y-sh);
36066 this.split.el.setWidth(box.width);
36068 if(this.collapsed){
36069 this.updateBody(box.width, null);
36071 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36075 Roo.bootstrap.layout.East = function(config){
36076 config.region = "east";
36077 config.cursor = "e-resize";
36078 Roo.bootstrap.layout.Split.call(this, config);
36080 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36081 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36082 this.split.el.addClass("roo-layout-split-h");
36084 var size = config.initialSize || config.width;
36085 if(typeof size != "undefined"){
36086 this.el.setWidth(size);
36089 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36090 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36091 getBox : function(){
36092 if(this.collapsed){
36093 return this.collapsedEl.getBox();
36095 var box = this.el.getBox();
36097 var sw = this.split.el.getWidth();
36104 updateBox : function(box){
36105 if(this.split && !this.collapsed){
36106 var sw = this.split.el.getWidth();
36108 this.split.el.setLeft(box.x);
36109 this.split.el.setTop(box.y);
36110 this.split.el.setHeight(box.height);
36113 if(this.collapsed){
36114 this.updateBody(null, box.height);
36116 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36120 Roo.bootstrap.layout.West = function(config){
36121 config.region = "west";
36122 config.cursor = "w-resize";
36124 Roo.bootstrap.layout.Split.call(this, config);
36126 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36127 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36128 this.split.el.addClass("roo-layout-split-h");
36132 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36133 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36135 onRender: function(ctr, pos)
36137 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36138 var size = this.config.initialSize || this.config.width;
36139 if(typeof size != "undefined"){
36140 this.el.setWidth(size);
36144 getBox : function(){
36145 if(this.collapsed){
36146 return this.collapsedEl.getBox();
36148 var box = this.el.getBox();
36150 box.width += this.split.el.getWidth();
36155 updateBox : function(box){
36156 if(this.split && !this.collapsed){
36157 var sw = this.split.el.getWidth();
36159 this.split.el.setLeft(box.x+box.width);
36160 this.split.el.setTop(box.y);
36161 this.split.el.setHeight(box.height);
36163 if(this.collapsed){
36164 this.updateBody(null, box.height);
36166 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36169 Roo.namespace("Roo.bootstrap.panel");/*
36171 * Ext JS Library 1.1.1
36172 * Copyright(c) 2006-2007, Ext JS, LLC.
36174 * Originally Released Under LGPL - original licence link has changed is not relivant.
36177 * <script type="text/javascript">
36180 * @class Roo.ContentPanel
36181 * @extends Roo.util.Observable
36182 * A basic ContentPanel element.
36183 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
36184 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
36185 * @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
36186 * @cfg {Boolean} closable True if the panel can be closed/removed
36187 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
36188 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36189 * @cfg {Toolbar} toolbar A toolbar for this panel
36190 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
36191 * @cfg {String} title The title for this panel
36192 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36193 * @cfg {String} url Calls {@link #setUrl} with this value
36194 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36195 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
36196 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
36197 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
36198 * @cfg {Boolean} badges render the badges
36201 * Create a new ContentPanel.
36202 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36203 * @param {String/Object} config A string to set only the title or a config object
36204 * @param {String} content (optional) Set the HTML content for this panel
36205 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36207 Roo.bootstrap.panel.Content = function( config){
36209 this.tpl = config.tpl || false;
36211 var el = config.el;
36212 var content = config.content;
36214 if(config.autoCreate){ // xtype is available if this is called from factory
36217 this.el = Roo.get(el);
36218 if(!this.el && config && config.autoCreate){
36219 if(typeof config.autoCreate == "object"){
36220 if(!config.autoCreate.id){
36221 config.autoCreate.id = config.id||el;
36223 this.el = Roo.DomHelper.append(document.body,
36224 config.autoCreate, true);
36226 var elcfg = { tag: "div",
36227 cls: "roo-layout-inactive-content",
36231 elcfg.html = config.html;
36235 this.el = Roo.DomHelper.append(document.body, elcfg , true);
36238 this.closable = false;
36239 this.loaded = false;
36240 this.active = false;
36243 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36245 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36247 this.wrapEl = this.el; //this.el.wrap();
36249 if (config.toolbar.items) {
36250 ti = config.toolbar.items ;
36251 delete config.toolbar.items ;
36255 this.toolbar.render(this.wrapEl, 'before');
36256 for(var i =0;i < ti.length;i++) {
36257 // Roo.log(['add child', items[i]]);
36258 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36260 this.toolbar.items = nitems;
36261 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36262 delete config.toolbar;
36266 // xtype created footer. - not sure if will work as we normally have to render first..
36267 if (this.footer && !this.footer.el && this.footer.xtype) {
36268 if (!this.wrapEl) {
36269 this.wrapEl = this.el.wrap();
36272 this.footer.container = this.wrapEl.createChild();
36274 this.footer = Roo.factory(this.footer, Roo);
36279 if(typeof config == "string"){
36280 this.title = config;
36282 Roo.apply(this, config);
36286 this.resizeEl = Roo.get(this.resizeEl, true);
36288 this.resizeEl = this.el;
36290 // handle view.xtype
36298 * Fires when this panel is activated.
36299 * @param {Roo.ContentPanel} this
36303 * @event deactivate
36304 * Fires when this panel is activated.
36305 * @param {Roo.ContentPanel} this
36307 "deactivate" : true,
36311 * Fires when this panel is resized if fitToFrame is true.
36312 * @param {Roo.ContentPanel} this
36313 * @param {Number} width The width after any component adjustments
36314 * @param {Number} height The height after any component adjustments
36320 * Fires when this tab is created
36321 * @param {Roo.ContentPanel} this
36332 if(this.autoScroll){
36333 this.resizeEl.setStyle("overflow", "auto");
36335 // fix randome scrolling
36336 //this.el.on('scroll', function() {
36337 // Roo.log('fix random scolling');
36338 // this.scrollTo('top',0);
36341 content = content || this.content;
36343 this.setContent(content);
36345 if(config && config.url){
36346 this.setUrl(this.url, this.params, this.loadOnce);
36351 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36353 if (this.view && typeof(this.view.xtype) != 'undefined') {
36354 this.view.el = this.el.appendChild(document.createElement("div"));
36355 this.view = Roo.factory(this.view);
36356 this.view.render && this.view.render(false, '');
36360 this.fireEvent('render', this);
36363 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36367 setRegion : function(region){
36368 this.region = region;
36369 this.setActiveClass(region && !this.background);
36373 setActiveClass: function(state)
36376 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36377 this.el.setStyle('position','relative');
36379 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36380 this.el.setStyle('position', 'absolute');
36385 * Returns the toolbar for this Panel if one was configured.
36386 * @return {Roo.Toolbar}
36388 getToolbar : function(){
36389 return this.toolbar;
36392 setActiveState : function(active)
36394 this.active = active;
36395 this.setActiveClass(active);
36397 this.fireEvent("deactivate", this);
36399 this.fireEvent("activate", this);
36403 * Updates this panel's element
36404 * @param {String} content The new content
36405 * @param {Boolean} loadScripts (optional) true to look for and process scripts
36407 setContent : function(content, loadScripts){
36408 this.el.update(content, loadScripts);
36411 ignoreResize : function(w, h){
36412 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
36415 this.lastSize = {width: w, height: h};
36420 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
36421 * @return {Roo.UpdateManager} The UpdateManager
36423 getUpdateManager : function(){
36424 return this.el.getUpdateManager();
36427 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
36428 * @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:
36431 url: "your-url.php",
36432 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
36433 callback: yourFunction,
36434 scope: yourObject, //(optional scope)
36437 text: "Loading...",
36442 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
36443 * 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.
36444 * @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}
36445 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
36446 * @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.
36447 * @return {Roo.ContentPanel} this
36450 var um = this.el.getUpdateManager();
36451 um.update.apply(um, arguments);
36457 * 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.
36458 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
36459 * @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)
36460 * @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)
36461 * @return {Roo.UpdateManager} The UpdateManager
36463 setUrl : function(url, params, loadOnce){
36464 if(this.refreshDelegate){
36465 this.removeListener("activate", this.refreshDelegate);
36467 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
36468 this.on("activate", this.refreshDelegate);
36469 return this.el.getUpdateManager();
36472 _handleRefresh : function(url, params, loadOnce){
36473 if(!loadOnce || !this.loaded){
36474 var updater = this.el.getUpdateManager();
36475 updater.update(url, params, this._setLoaded.createDelegate(this));
36479 _setLoaded : function(){
36480 this.loaded = true;
36484 * Returns this panel's id
36487 getId : function(){
36492 * Returns this panel's element - used by regiosn to add.
36493 * @return {Roo.Element}
36495 getEl : function(){
36496 return this.wrapEl || this.el;
36501 adjustForComponents : function(width, height)
36503 //Roo.log('adjustForComponents ');
36504 if(this.resizeEl != this.el){
36505 width -= this.el.getFrameWidth('lr');
36506 height -= this.el.getFrameWidth('tb');
36509 var te = this.toolbar.getEl();
36510 te.setWidth(width);
36511 height -= te.getHeight();
36514 var te = this.footer.getEl();
36515 te.setWidth(width);
36516 height -= te.getHeight();
36520 if(this.adjustments){
36521 width += this.adjustments[0];
36522 height += this.adjustments[1];
36524 return {"width": width, "height": height};
36527 setSize : function(width, height){
36528 if(this.fitToFrame && !this.ignoreResize(width, height)){
36529 if(this.fitContainer && this.resizeEl != this.el){
36530 this.el.setSize(width, height);
36532 var size = this.adjustForComponents(width, height);
36533 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
36534 this.fireEvent('resize', this, size.width, size.height);
36539 * Returns this panel's title
36542 getTitle : function(){
36544 if (typeof(this.title) != 'object') {
36549 for (var k in this.title) {
36550 if (!this.title.hasOwnProperty(k)) {
36554 if (k.indexOf('-') >= 0) {
36555 var s = k.split('-');
36556 for (var i = 0; i<s.length; i++) {
36557 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
36560 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
36567 * Set this panel's title
36568 * @param {String} title
36570 setTitle : function(title){
36571 this.title = title;
36573 this.region.updatePanelTitle(this, title);
36578 * Returns true is this panel was configured to be closable
36579 * @return {Boolean}
36581 isClosable : function(){
36582 return this.closable;
36585 beforeSlide : function(){
36587 this.resizeEl.clip();
36590 afterSlide : function(){
36592 this.resizeEl.unclip();
36596 * Force a content refresh from the URL specified in the {@link #setUrl} method.
36597 * Will fail silently if the {@link #setUrl} method has not been called.
36598 * This does not activate the panel, just updates its content.
36600 refresh : function(){
36601 if(this.refreshDelegate){
36602 this.loaded = false;
36603 this.refreshDelegate();
36608 * Destroys this panel
36610 destroy : function(){
36611 this.el.removeAllListeners();
36612 var tempEl = document.createElement("span");
36613 tempEl.appendChild(this.el.dom);
36614 tempEl.innerHTML = "";
36620 * form - if the content panel contains a form - this is a reference to it.
36621 * @type {Roo.form.Form}
36625 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
36626 * This contains a reference to it.
36632 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
36642 * @param {Object} cfg Xtype definition of item to add.
36646 getChildContainer: function () {
36647 return this.getEl();
36652 var ret = new Roo.factory(cfg);
36657 if (cfg.xtype.match(/^Form$/)) {
36660 //if (this.footer) {
36661 // el = this.footer.container.insertSibling(false, 'before');
36663 el = this.el.createChild();
36666 this.form = new Roo.form.Form(cfg);
36669 if ( this.form.allItems.length) {
36670 this.form.render(el.dom);
36674 // should only have one of theses..
36675 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
36676 // views.. should not be just added - used named prop 'view''
36678 cfg.el = this.el.appendChild(document.createElement("div"));
36681 var ret = new Roo.factory(cfg);
36683 ret.render && ret.render(false, ''); // render blank..
36693 * @class Roo.bootstrap.panel.Grid
36694 * @extends Roo.bootstrap.panel.Content
36696 * Create a new GridPanel.
36697 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
36698 * @param {Object} config A the config object
36704 Roo.bootstrap.panel.Grid = function(config)
36708 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
36709 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
36711 config.el = this.wrapper;
36712 //this.el = this.wrapper;
36714 if (config.container) {
36715 // ctor'ed from a Border/panel.grid
36718 this.wrapper.setStyle("overflow", "hidden");
36719 this.wrapper.addClass('roo-grid-container');
36724 if(config.toolbar){
36725 var tool_el = this.wrapper.createChild();
36726 this.toolbar = Roo.factory(config.toolbar);
36728 if (config.toolbar.items) {
36729 ti = config.toolbar.items ;
36730 delete config.toolbar.items ;
36734 this.toolbar.render(tool_el);
36735 for(var i =0;i < ti.length;i++) {
36736 // Roo.log(['add child', items[i]]);
36737 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36739 this.toolbar.items = nitems;
36741 delete config.toolbar;
36744 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
36745 config.grid.scrollBody = true;;
36746 config.grid.monitorWindowResize = false; // turn off autosizing
36747 config.grid.autoHeight = false;
36748 config.grid.autoWidth = false;
36750 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
36752 if (config.background) {
36753 // render grid on panel activation (if panel background)
36754 this.on('activate', function(gp) {
36755 if (!gp.grid.rendered) {
36756 gp.grid.render(this.wrapper);
36757 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
36762 this.grid.render(this.wrapper);
36763 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
36766 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
36767 // ??? needed ??? config.el = this.wrapper;
36772 // xtype created footer. - not sure if will work as we normally have to render first..
36773 if (this.footer && !this.footer.el && this.footer.xtype) {
36775 var ctr = this.grid.getView().getFooterPanel(true);
36776 this.footer.dataSource = this.grid.dataSource;
36777 this.footer = Roo.factory(this.footer, Roo);
36778 this.footer.render(ctr);
36788 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
36789 getId : function(){
36790 return this.grid.id;
36794 * Returns the grid for this panel
36795 * @return {Roo.bootstrap.Table}
36797 getGrid : function(){
36801 setSize : function(width, height){
36802 if(!this.ignoreResize(width, height)){
36803 var grid = this.grid;
36804 var size = this.adjustForComponents(width, height);
36805 var gridel = grid.getGridEl();
36806 gridel.setSize(size.width, size.height);
36808 var thd = grid.getGridEl().select('thead',true).first();
36809 var tbd = grid.getGridEl().select('tbody', true).first();
36811 tbd.setSize(width, height - thd.getHeight());
36820 beforeSlide : function(){
36821 this.grid.getView().scroller.clip();
36824 afterSlide : function(){
36825 this.grid.getView().scroller.unclip();
36828 destroy : function(){
36829 this.grid.destroy();
36831 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
36836 * @class Roo.bootstrap.panel.Nest
36837 * @extends Roo.bootstrap.panel.Content
36839 * Create a new Panel, that can contain a layout.Border.
36842 * @param {Roo.BorderLayout} layout The layout for this panel
36843 * @param {String/Object} config A string to set only the title or a config object
36845 Roo.bootstrap.panel.Nest = function(config)
36847 // construct with only one argument..
36848 /* FIXME - implement nicer consturctors
36849 if (layout.layout) {
36851 layout = config.layout;
36852 delete config.layout;
36854 if (layout.xtype && !layout.getEl) {
36855 // then layout needs constructing..
36856 layout = Roo.factory(layout, Roo);
36860 config.el = config.layout.getEl();
36862 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
36864 config.layout.monitorWindowResize = false; // turn off autosizing
36865 this.layout = config.layout;
36866 this.layout.getEl().addClass("roo-layout-nested-layout");
36873 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
36875 setSize : function(width, height){
36876 if(!this.ignoreResize(width, height)){
36877 var size = this.adjustForComponents(width, height);
36878 var el = this.layout.getEl();
36879 if (size.height < 1) {
36880 el.setWidth(size.width);
36882 el.setSize(size.width, size.height);
36884 var touch = el.dom.offsetWidth;
36885 this.layout.layout();
36886 // ie requires a double layout on the first pass
36887 if(Roo.isIE && !this.initialized){
36888 this.initialized = true;
36889 this.layout.layout();
36894 // activate all subpanels if not currently active..
36896 setActiveState : function(active){
36897 this.active = active;
36898 this.setActiveClass(active);
36901 this.fireEvent("deactivate", this);
36905 this.fireEvent("activate", this);
36906 // not sure if this should happen before or after..
36907 if (!this.layout) {
36908 return; // should not happen..
36911 for (var r in this.layout.regions) {
36912 reg = this.layout.getRegion(r);
36913 if (reg.getActivePanel()) {
36914 //reg.showPanel(reg.getActivePanel()); // force it to activate..
36915 reg.setActivePanel(reg.getActivePanel());
36918 if (!reg.panels.length) {
36921 reg.showPanel(reg.getPanel(0));
36930 * Returns the nested BorderLayout for this panel
36931 * @return {Roo.BorderLayout}
36933 getLayout : function(){
36934 return this.layout;
36938 * Adds a xtype elements to the layout of the nested panel
36942 xtype : 'ContentPanel',
36949 xtype : 'NestedLayoutPanel',
36955 items : [ ... list of content panels or nested layout panels.. ]
36959 * @param {Object} cfg Xtype definition of item to add.
36961 addxtype : function(cfg) {
36962 return this.layout.addxtype(cfg);
36967 * Ext JS Library 1.1.1
36968 * Copyright(c) 2006-2007, Ext JS, LLC.
36970 * Originally Released Under LGPL - original licence link has changed is not relivant.
36973 * <script type="text/javascript">
36976 * @class Roo.TabPanel
36977 * @extends Roo.util.Observable
36978 * A lightweight tab container.
36982 // basic tabs 1, built from existing content
36983 var tabs = new Roo.TabPanel("tabs1");
36984 tabs.addTab("script", "View Script");
36985 tabs.addTab("markup", "View Markup");
36986 tabs.activate("script");
36988 // more advanced tabs, built from javascript
36989 var jtabs = new Roo.TabPanel("jtabs");
36990 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
36992 // set up the UpdateManager
36993 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
36994 var updater = tab2.getUpdateManager();
36995 updater.setDefaultUrl("ajax1.htm");
36996 tab2.on('activate', updater.refresh, updater, true);
36998 // Use setUrl for Ajax loading
36999 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37000 tab3.setUrl("ajax2.htm", null, true);
37003 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37006 jtabs.activate("jtabs-1");
37009 * Create a new TabPanel.
37010 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37011 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37013 Roo.bootstrap.panel.Tabs = function(config){
37015 * The container element for this TabPanel.
37016 * @type Roo.Element
37018 this.el = Roo.get(config.el);
37021 if(typeof config == "boolean"){
37022 this.tabPosition = config ? "bottom" : "top";
37024 Roo.apply(this, config);
37028 if(this.tabPosition == "bottom"){
37029 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37030 this.el.addClass("roo-tabs-bottom");
37032 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37033 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37034 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37036 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37038 if(this.tabPosition != "bottom"){
37039 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37040 * @type Roo.Element
37042 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37043 this.el.addClass("roo-tabs-top");
37047 this.bodyEl.setStyle("position", "relative");
37049 this.active = null;
37050 this.activateDelegate = this.activate.createDelegate(this);
37055 * Fires when the active tab changes
37056 * @param {Roo.TabPanel} this
37057 * @param {Roo.TabPanelItem} activePanel The new active tab
37061 * @event beforetabchange
37062 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37063 * @param {Roo.TabPanel} this
37064 * @param {Object} e Set cancel to true on this object to cancel the tab change
37065 * @param {Roo.TabPanelItem} tab The tab being changed to
37067 "beforetabchange" : true
37070 Roo.EventManager.onWindowResize(this.onResize, this);
37071 this.cpad = this.el.getPadding("lr");
37072 this.hiddenCount = 0;
37075 // toolbar on the tabbar support...
37076 if (this.toolbar) {
37077 alert("no toolbar support yet");
37078 this.toolbar = false;
37080 var tcfg = this.toolbar;
37081 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
37082 this.toolbar = new Roo.Toolbar(tcfg);
37083 if (Roo.isSafari) {
37084 var tbl = tcfg.container.child('table', true);
37085 tbl.setAttribute('width', '100%');
37093 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37096 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37098 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37100 tabPosition : "top",
37102 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37104 currentTabWidth : 0,
37106 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37110 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37114 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37116 preferredTabWidth : 175,
37118 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37120 resizeTabs : false,
37122 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37124 monitorResize : true,
37126 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
37131 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37132 * @param {String} id The id of the div to use <b>or create</b>
37133 * @param {String} text The text for the tab
37134 * @param {String} content (optional) Content to put in the TabPanelItem body
37135 * @param {Boolean} closable (optional) True to create a close icon on the tab
37136 * @return {Roo.TabPanelItem} The created TabPanelItem
37138 addTab : function(id, text, content, closable, tpl)
37140 var item = new Roo.bootstrap.panel.TabItem({
37144 closable : closable,
37147 this.addTabItem(item);
37149 item.setContent(content);
37155 * Returns the {@link Roo.TabPanelItem} with the specified id/index
37156 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37157 * @return {Roo.TabPanelItem}
37159 getTab : function(id){
37160 return this.items[id];
37164 * Hides the {@link Roo.TabPanelItem} with the specified id/index
37165 * @param {String/Number} id The id or index of the TabPanelItem to hide.
37167 hideTab : function(id){
37168 var t = this.items[id];
37171 this.hiddenCount++;
37172 this.autoSizeTabs();
37177 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37178 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37180 unhideTab : function(id){
37181 var t = this.items[id];
37183 t.setHidden(false);
37184 this.hiddenCount--;
37185 this.autoSizeTabs();
37190 * Adds an existing {@link Roo.TabPanelItem}.
37191 * @param {Roo.TabPanelItem} item The TabPanelItem to add
37193 addTabItem : function(item){
37194 this.items[item.id] = item;
37195 this.items.push(item);
37196 // if(this.resizeTabs){
37197 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37198 // this.autoSizeTabs();
37200 // item.autoSize();
37205 * Removes a {@link Roo.TabPanelItem}.
37206 * @param {String/Number} id The id or index of the TabPanelItem to remove.
37208 removeTab : function(id){
37209 var items = this.items;
37210 var tab = items[id];
37211 if(!tab) { return; }
37212 var index = items.indexOf(tab);
37213 if(this.active == tab && items.length > 1){
37214 var newTab = this.getNextAvailable(index);
37219 this.stripEl.dom.removeChild(tab.pnode.dom);
37220 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37221 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37223 items.splice(index, 1);
37224 delete this.items[tab.id];
37225 tab.fireEvent("close", tab);
37226 tab.purgeListeners();
37227 this.autoSizeTabs();
37230 getNextAvailable : function(start){
37231 var items = this.items;
37233 // look for a next tab that will slide over to
37234 // replace the one being removed
37235 while(index < items.length){
37236 var item = items[++index];
37237 if(item && !item.isHidden()){
37241 // if one isn't found select the previous tab (on the left)
37244 var item = items[--index];
37245 if(item && !item.isHidden()){
37253 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37254 * @param {String/Number} id The id or index of the TabPanelItem to disable.
37256 disableTab : function(id){
37257 var tab = this.items[id];
37258 if(tab && this.active != tab){
37264 * Enables a {@link Roo.TabPanelItem} that is disabled.
37265 * @param {String/Number} id The id or index of the TabPanelItem to enable.
37267 enableTab : function(id){
37268 var tab = this.items[id];
37273 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37274 * @param {String/Number} id The id or index of the TabPanelItem to activate.
37275 * @return {Roo.TabPanelItem} The TabPanelItem.
37277 activate : function(id){
37278 var tab = this.items[id];
37282 if(tab == this.active || tab.disabled){
37286 this.fireEvent("beforetabchange", this, e, tab);
37287 if(e.cancel !== true && !tab.disabled){
37289 this.active.hide();
37291 this.active = this.items[id];
37292 this.active.show();
37293 this.fireEvent("tabchange", this, this.active);
37299 * Gets the active {@link Roo.TabPanelItem}.
37300 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37302 getActiveTab : function(){
37303 return this.active;
37307 * Updates the tab body element to fit the height of the container element
37308 * for overflow scrolling
37309 * @param {Number} targetHeight (optional) Override the starting height from the elements height
37311 syncHeight : function(targetHeight){
37312 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37313 var bm = this.bodyEl.getMargins();
37314 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37315 this.bodyEl.setHeight(newHeight);
37319 onResize : function(){
37320 if(this.monitorResize){
37321 this.autoSizeTabs();
37326 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37328 beginUpdate : function(){
37329 this.updating = true;
37333 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37335 endUpdate : function(){
37336 this.updating = false;
37337 this.autoSizeTabs();
37341 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37343 autoSizeTabs : function(){
37344 var count = this.items.length;
37345 var vcount = count - this.hiddenCount;
37346 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37349 var w = Math.max(this.el.getWidth() - this.cpad, 10);
37350 var availWidth = Math.floor(w / vcount);
37351 var b = this.stripBody;
37352 if(b.getWidth() > w){
37353 var tabs = this.items;
37354 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37355 if(availWidth < this.minTabWidth){
37356 /*if(!this.sleft){ // incomplete scrolling code
37357 this.createScrollButtons();
37360 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37363 if(this.currentTabWidth < this.preferredTabWidth){
37364 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37370 * Returns the number of tabs in this TabPanel.
37373 getCount : function(){
37374 return this.items.length;
37378 * Resizes all the tabs to the passed width
37379 * @param {Number} The new width
37381 setTabWidth : function(width){
37382 this.currentTabWidth = width;
37383 for(var i = 0, len = this.items.length; i < len; i++) {
37384 if(!this.items[i].isHidden()) {
37385 this.items[i].setWidth(width);
37391 * Destroys this TabPanel
37392 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
37394 destroy : function(removeEl){
37395 Roo.EventManager.removeResizeListener(this.onResize, this);
37396 for(var i = 0, len = this.items.length; i < len; i++){
37397 this.items[i].purgeListeners();
37399 if(removeEl === true){
37400 this.el.update("");
37405 createStrip : function(container)
37407 var strip = document.createElement("nav");
37408 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
37409 container.appendChild(strip);
37413 createStripList : function(strip)
37415 // div wrapper for retard IE
37416 // returns the "tr" element.
37417 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
37418 //'<div class="x-tabs-strip-wrap">'+
37419 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
37420 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
37421 return strip.firstChild; //.firstChild.firstChild.firstChild;
37423 createBody : function(container)
37425 var body = document.createElement("div");
37426 Roo.id(body, "tab-body");
37427 //Roo.fly(body).addClass("x-tabs-body");
37428 Roo.fly(body).addClass("tab-content");
37429 container.appendChild(body);
37432 createItemBody :function(bodyEl, id){
37433 var body = Roo.getDom(id);
37435 body = document.createElement("div");
37438 //Roo.fly(body).addClass("x-tabs-item-body");
37439 Roo.fly(body).addClass("tab-pane");
37440 bodyEl.insertBefore(body, bodyEl.firstChild);
37444 createStripElements : function(stripEl, text, closable, tpl)
37446 var td = document.createElement("li"); // was td..
37449 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
37452 stripEl.appendChild(td);
37454 td.className = "x-tabs-closable";
37455 if(!this.closeTpl){
37456 this.closeTpl = new Roo.Template(
37457 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37458 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
37459 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
37462 var el = this.closeTpl.overwrite(td, {"text": text});
37463 var close = el.getElementsByTagName("div")[0];
37464 var inner = el.getElementsByTagName("em")[0];
37465 return {"el": el, "close": close, "inner": inner};
37468 // not sure what this is..
37469 // if(!this.tabTpl){
37470 //this.tabTpl = new Roo.Template(
37471 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37472 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
37474 // this.tabTpl = new Roo.Template(
37475 // '<a href="#">' +
37476 // '<span unselectable="on"' +
37477 // (this.disableTooltips ? '' : ' title="{text}"') +
37478 // ' >{text}</span></a>'
37484 var template = tpl || this.tabTpl || false;
37488 template = new Roo.Template(
37490 '<span unselectable="on"' +
37491 (this.disableTooltips ? '' : ' title="{text}"') +
37492 ' >{text}</span></a>'
37496 switch (typeof(template)) {
37500 template = new Roo.Template(template);
37506 var el = template.overwrite(td, {"text": text});
37508 var inner = el.getElementsByTagName("span")[0];
37510 return {"el": el, "inner": inner};
37518 * @class Roo.TabPanelItem
37519 * @extends Roo.util.Observable
37520 * Represents an individual item (tab plus body) in a TabPanel.
37521 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
37522 * @param {String} id The id of this TabPanelItem
37523 * @param {String} text The text for the tab of this TabPanelItem
37524 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
37526 Roo.bootstrap.panel.TabItem = function(config){
37528 * The {@link Roo.TabPanel} this TabPanelItem belongs to
37529 * @type Roo.TabPanel
37531 this.tabPanel = config.panel;
37533 * The id for this TabPanelItem
37536 this.id = config.id;
37538 this.disabled = false;
37540 this.text = config.text;
37542 this.loaded = false;
37543 this.closable = config.closable;
37546 * The body element for this TabPanelItem.
37547 * @type Roo.Element
37549 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
37550 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
37551 this.bodyEl.setStyle("display", "block");
37552 this.bodyEl.setStyle("zoom", "1");
37553 //this.hideAction();
37555 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
37557 this.el = Roo.get(els.el);
37558 this.inner = Roo.get(els.inner, true);
37559 this.textEl = Roo.get(this.el.dom.firstChild, true);
37560 this.pnode = Roo.get(els.el.parentNode, true);
37561 this.el.on("mousedown", this.onTabMouseDown, this);
37562 this.el.on("click", this.onTabClick, this);
37564 if(config.closable){
37565 var c = Roo.get(els.close, true);
37566 c.dom.title = this.closeText;
37567 c.addClassOnOver("close-over");
37568 c.on("click", this.closeClick, this);
37574 * Fires when this tab becomes the active tab.
37575 * @param {Roo.TabPanel} tabPanel The parent TabPanel
37576 * @param {Roo.TabPanelItem} this
37580 * @event beforeclose
37581 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
37582 * @param {Roo.TabPanelItem} this
37583 * @param {Object} e Set cancel to true on this object to cancel the close.
37585 "beforeclose": true,
37588 * Fires when this tab is closed.
37589 * @param {Roo.TabPanelItem} this
37593 * @event deactivate
37594 * Fires when this tab is no longer the active tab.
37595 * @param {Roo.TabPanel} tabPanel The parent TabPanel
37596 * @param {Roo.TabPanelItem} this
37598 "deactivate" : true
37600 this.hidden = false;
37602 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
37605 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
37607 purgeListeners : function(){
37608 Roo.util.Observable.prototype.purgeListeners.call(this);
37609 this.el.removeAllListeners();
37612 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
37615 this.pnode.addClass("active");
37618 this.tabPanel.stripWrap.repaint();
37620 this.fireEvent("activate", this.tabPanel, this);
37624 * Returns true if this tab is the active tab.
37625 * @return {Boolean}
37627 isActive : function(){
37628 return this.tabPanel.getActiveTab() == this;
37632 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
37635 this.pnode.removeClass("active");
37637 this.fireEvent("deactivate", this.tabPanel, this);
37640 hideAction : function(){
37641 this.bodyEl.hide();
37642 this.bodyEl.setStyle("position", "absolute");
37643 this.bodyEl.setLeft("-20000px");
37644 this.bodyEl.setTop("-20000px");
37647 showAction : function(){
37648 this.bodyEl.setStyle("position", "relative");
37649 this.bodyEl.setTop("");
37650 this.bodyEl.setLeft("");
37651 this.bodyEl.show();
37655 * Set the tooltip for the tab.
37656 * @param {String} tooltip The tab's tooltip
37658 setTooltip : function(text){
37659 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
37660 this.textEl.dom.qtip = text;
37661 this.textEl.dom.removeAttribute('title');
37663 this.textEl.dom.title = text;
37667 onTabClick : function(e){
37668 e.preventDefault();
37669 this.tabPanel.activate(this.id);
37672 onTabMouseDown : function(e){
37673 e.preventDefault();
37674 this.tabPanel.activate(this.id);
37677 getWidth : function(){
37678 return this.inner.getWidth();
37681 setWidth : function(width){
37682 var iwidth = width - this.pnode.getPadding("lr");
37683 this.inner.setWidth(iwidth);
37684 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
37685 this.pnode.setWidth(width);
37689 * Show or hide the tab
37690 * @param {Boolean} hidden True to hide or false to show.
37692 setHidden : function(hidden){
37693 this.hidden = hidden;
37694 this.pnode.setStyle("display", hidden ? "none" : "");
37698 * Returns true if this tab is "hidden"
37699 * @return {Boolean}
37701 isHidden : function(){
37702 return this.hidden;
37706 * Returns the text for this tab
37709 getText : function(){
37713 autoSize : function(){
37714 //this.el.beginMeasure();
37715 this.textEl.setWidth(1);
37717 * #2804 [new] Tabs in Roojs
37718 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
37720 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
37721 //this.el.endMeasure();
37725 * Sets the text for the tab (Note: this also sets the tooltip text)
37726 * @param {String} text The tab's text and tooltip
37728 setText : function(text){
37730 this.textEl.update(text);
37731 this.setTooltip(text);
37732 //if(!this.tabPanel.resizeTabs){
37733 // this.autoSize();
37737 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
37739 activate : function(){
37740 this.tabPanel.activate(this.id);
37744 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
37746 disable : function(){
37747 if(this.tabPanel.active != this){
37748 this.disabled = true;
37749 this.pnode.addClass("disabled");
37754 * Enables this TabPanelItem if it was previously disabled.
37756 enable : function(){
37757 this.disabled = false;
37758 this.pnode.removeClass("disabled");
37762 * Sets the content for this TabPanelItem.
37763 * @param {String} content The content
37764 * @param {Boolean} loadScripts true to look for and load scripts
37766 setContent : function(content, loadScripts){
37767 this.bodyEl.update(content, loadScripts);
37771 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
37772 * @return {Roo.UpdateManager} The UpdateManager
37774 getUpdateManager : function(){
37775 return this.bodyEl.getUpdateManager();
37779 * Set a URL to be used to load the content for this TabPanelItem.
37780 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
37781 * @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)
37782 * @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)
37783 * @return {Roo.UpdateManager} The UpdateManager
37785 setUrl : function(url, params, loadOnce){
37786 if(this.refreshDelegate){
37787 this.un('activate', this.refreshDelegate);
37789 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37790 this.on("activate", this.refreshDelegate);
37791 return this.bodyEl.getUpdateManager();
37795 _handleRefresh : function(url, params, loadOnce){
37796 if(!loadOnce || !this.loaded){
37797 var updater = this.bodyEl.getUpdateManager();
37798 updater.update(url, params, this._setLoaded.createDelegate(this));
37803 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
37804 * Will fail silently if the setUrl method has not been called.
37805 * This does not activate the panel, just updates its content.
37807 refresh : function(){
37808 if(this.refreshDelegate){
37809 this.loaded = false;
37810 this.refreshDelegate();
37815 _setLoaded : function(){
37816 this.loaded = true;
37820 closeClick : function(e){
37823 this.fireEvent("beforeclose", this, o);
37824 if(o.cancel !== true){
37825 this.tabPanel.removeTab(this.id);
37829 * The text displayed in the tooltip for the close icon.
37832 closeText : "Close this tab"
37842 * @class Roo.bootstrap.PhoneInput
37843 * @extends Roo.bootstrap.TriggerField
37844 * Bootstrap PhoneInput class
37847 * Create a new PhoneInput
37848 * @param {Object} config The config object
37851 Roo.bootstrap.PhoneInput = function(config){
37853 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
37858 * Fires when the dropdown list is expanded
37859 * @param {Roo.bootstrap.ComboBox} combo This combo box
37864 * Fires when the dropdown list is collapsed
37865 * @param {Roo.bootstrap.ComboBox} combo This combo box
37869 * @event beforeselect
37870 * Fires before a list item is selected. Return false to cancel the selection.
37871 * @param {Roo.bootstrap.ComboBox} combo This combo box
37872 * @param {Roo.data.Record} record The data record returned from the underlying store
37873 * @param {Number} index The index of the selected item in the dropdown list
37875 'beforeselect' : true,
37878 * Fires when a list item is selected
37879 * @param {Roo.bootstrap.ComboBox} combo This combo box
37880 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
37881 * @param {Number} index The index of the selected item in the dropdown list
37885 * @event beforequery
37886 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
37887 * The event object passed has these properties:
37888 * @param {Roo.bootstrap.ComboBox} combo This combo box
37889 * @param {String} query The query
37890 * @param {Boolean} forceAll true to force "all" query
37891 * @param {Boolean} cancel true to cancel the query
37892 * @param {Object} e The query event object
37894 'beforequery': true,
37897 * Fires when the 'add' icon is pressed (add a listener to enable add button)
37898 * @param {Roo.bootstrap.ComboBox} combo This combo box
37903 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
37904 * @param {Roo.bootstrap.ComboBox} combo This combo box
37905 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
37910 * Fires when the remove value from the combobox array
37911 * @param {Roo.bootstrap.ComboBox} combo This combo box
37915 * @event afterremove
37916 * Fires when the remove value from the combobox array
37917 * @param {Roo.bootstrap.ComboBox} combo This combo box
37919 'afterremove' : true,
37921 * @event specialfilter
37922 * Fires when specialfilter
37923 * @param {Roo.bootstrap.ComboBox} combo This combo box
37925 'touchviewdisplay' : true
37928 this.country = []; //fetch country JSON
37931 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
37933 listWidth: undefined,
37937 selectedClass: 'active',
37943 triggerAction: 'query',
37945 validClass : "has-success",
37947 invalidClass: "has-warning",
37950 defaultCountry: 'hk',
37952 preferedCountries: undefined, //array
37954 filterCountries: undefined, //array
37956 displayMode: undefined, //string
37958 getAutoCreate : function(){
37960 this.list = Roo.bootstrap.PhoneInput.List;
37962 if(this.filterCountries) {
37963 for(var i = 0; i < this.filterCountries.length; i++) {
37964 delete this.list[this.filterCountries[i]];
37968 if (this.preferedCountries) {
37972 var align = this.labelAlign || this.parentLabelAlign();
37974 var id = Roo.id(); //all el??
37983 type : this.inputType,
37984 cls : 'form-control',
37985 style: 'padding-left: 60px;',
37986 placeholder : this.placeholder || ''
37990 input.name = this.name;
37993 input.cls += ' input-' + this.size;
37996 if (this.disabled) {
37997 input.disabled=true;
38000 var inputblock = input;
38002 if(this.hasFeedback && !this.allowBlank){
38005 cls: 'glyphicon form-control-feedback'
38013 inputblock.cn.push(input);
38015 if(this.hasFeedback && !this.allowBlank){
38016 inputblock.cls += 'has-feedback';
38017 inputblock.cn.push(feedback);
38026 cls: 'form-hidden-field'
38035 style: 'margin-right:5px',
38036 cls: 'roo-selected-region',
38037 cn: [] //flag position ... (iti-flag-us)
38045 if (this.caret != false) {
38048 cls: 'fa fa-' + this.caret
38053 cls: 'roo-select2-container input-group',
38059 cls : 'input-group-addon btn dropdown-toggle',
38060 style : 'position: absolute; z-index: 4;background: none;border: none; margin-top: 4px; margin-left: 3px; margin-right: 3px;',
38066 cls: 'combobox-clear',
38077 combobox.cn.push(box);
38079 if (align ==='left' && this.fieldLabel.length) {
38081 cfg.cls += ' roo-form-group-label-left';
38086 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
38087 tooltip : 'This field is required'
38092 cls : 'control-label',
38093 html : this.fieldLabel
38104 var labelCfg = cfg.cn[1];
38105 var contentCfg = cfg.cn[2];
38107 if(this.indicatorpos == 'right'){
38112 cls : 'control-label',
38116 html : this.fieldLabel
38120 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
38121 tooltip : 'This field is required'
38134 labelCfg = cfg.cn[0];
38135 contentCfg = cfg.cn[1];
38138 if(this.labelWidth > 12){
38139 labelCfg.style = "width: " + this.labelWidth + 'px';
38142 if(this.labelWidth < 13 && this.labelmd == 0){
38143 this.labelmd = this.labelWidth;
38146 if(this.labellg > 0){
38147 labelCfg.cls += ' col-lg-' + this.labellg;
38148 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
38151 if(this.labelmd > 0){
38152 labelCfg.cls += ' col-md-' + this.labelmd;
38153 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
38156 if(this.labelsm > 0){
38157 labelCfg.cls += ' col-sm-' + this.labelsm;
38158 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
38161 if(this.labelxs > 0){
38162 labelCfg.cls += ' col-xs-' + this.labelxs;
38163 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
38166 } else if ( this.fieldLabel.length) {
38167 // Roo.log(" label");
38171 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
38172 tooltip : 'This field is required'
38176 //cls : 'input-group-addon',
38177 html : this.fieldLabel
38185 if(this.indicatorpos == 'right'){
38193 html : this.fieldLabel
38197 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
38198 tooltip : 'This field is required'
38210 ['xs','sm','md','lg'].map(function(size){
38211 if (settings[size]) {
38212 cfg.cls += ' col-' + size + '-' + settings[size];
38219 _initEventsCalled : false,
38221 initEvents: function()
38223 if (this._initEventsCalled) {
38227 this._initEventsCalled = true;
38229 this.store = new Roo.data.SimpleStore({
38231 fields : ['name','iso','dial_code','order','area_code']
38234 this.store = Roo.factory(this.store, Roo.data);
38235 this.store.parent = this;
38237 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
38242 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
38243 _this.list.setWidth(lw);
38246 this.list.on('mouseover', this.onViewOver, this);
38247 this.list.on('mousemove', this.onViewMove, this);
38248 this.list.on('scroll', this.onViewScroll, this);
38251 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
38254 this.view = new Roo.View(this.list, this.tpl, {
38255 singleSelect:true, store: this.store, selectedClass: this.selectedClass
38258 this.view.on('click', this.onViewClick, this);
38260 this.store.on('beforeload', this.onBeforeLoad, this);
38261 this.store.on('load', this.onLoad, this);
38262 this.store.on('loadexception', this.onLoadException, this);
38264 this.keyNav = new Roo.KeyNav(this.inputEl(), {
38265 "up" : function(e){
38266 this.inKeyMode = true;
38270 "down" : function(e){
38271 if(!this.isExpanded()){
38272 this.onTriggerClick();
38274 this.inKeyMode = true;
38279 "enter" : function(e){
38280 // this.onViewClick();
38284 if(this.fireEvent("specialkey", this, e)){
38285 this.onViewClick(false);
38291 "esc" : function(e){
38295 "tab" : function(e){
38298 if(this.fireEvent("specialkey", this, e)){
38299 this.onViewClick(false);
38307 doRelay : function(foo, bar, hname){
38308 if(hname == 'down' || this.scope.isExpanded()){
38309 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
38319 onViewOver : function(e, t){
38320 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
38323 var item = this.view.findItemFromChild(t);
38326 var index = this.view.indexOf(item);
38327 this.select(index, false);
38331 onViewMove : function(e, t){
38332 this.inKeyMode = false;
38335 onViewScroll : function(e, t){
38337 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){
38341 this.hasQuery = true;
38343 this.loading = this.list.select('.loading', true).first();
38345 if(this.loading === null){
38346 this.list.createChild({
38348 cls: 'loading roo-select2-more-results roo-select2-active',
38349 html: 'Loading more results...'
38352 this.loading = this.list.select('.loading', true).first();
38354 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
38356 this.loading.hide();
38359 this.loading.show();
38364 this.loadNext = true;
38366 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
38371 onTriggerClick : function(e)
38373 Roo.log('trigger click');
38375 if(this.disabled || !this.triggerList){
38380 this.loadNext = false;
38382 if(this.isExpanded()){
38384 if (!this.blockFocus) {
38385 this.inputEl().focus();
38389 this.hasFocus = true;
38390 if(this.triggerAction == 'all') {
38391 this.doQuery(this.allQuery, true);
38393 this.doQuery(this.getRawValue());
38395 if (!this.blockFocus) {
38396 this.inputEl().focus();
38403 Roo.apply(Roo.bootstrap.PhoneInput, {
38406 * iso2 and abbr for all countries
38410 ["Afghanistan (افغانستان)", "af", "93"],
38411 ["Albania (Shqipëri)", "al", "355"],
38412 ["Algeria (الجزائر)", "dz", "213"],
38413 ["American Samoa", "as", "1684"],
38414 ["Andorra", "ad", "376"],
38415 ["Angola", "ao", "244"],
38416 ["Anguilla", "ai", "1264"],
38417 ["Antigua and Barbuda", "ag", "1268"],
38418 ["Argentina", "ar", "54"],
38419 ["Armenia (Հայաստան)", "am", "374"],
38420 ["Aruba", "aw", "297"],
38421 ["Australia", "au", "61", 0],
38422 ["Austria (Österreich)", "at", "43"],
38423 ["Azerbaijan (Azərbaycan)", "az", "994"],
38424 ["Bahamas", "bs", "1242"],
38425 ["Bahrain (البحرين)", "bh", "973"],
38426 ["Bangladesh (বাংলাদেশ)", "bd", "880"],
38427 ["Barbados", "bb", "1246"],
38428 ["Belarus (Беларусь)", "by", "375"],
38429 ["Belgium (België)", "be", "32"],
38430 ["Belize", "bz", "501"],
38431 ["Benin (Bénin)", "bj", "229"],
38432 ["Bermuda", "bm", "1441"],
38433 ["Bhutan (འབྲུག)", "bt", "975"],
38434 ["Bolivia", "bo", "591"],
38435 ["Bosnia and Herzegovina (Босна и Херцеговина)", "ba", "387"],
38436 ["Botswana", "bw", "267"],
38437 ["Brazil (Brasil)", "br", "55"],
38438 ["British Indian Ocean Territory", "io", "246"],
38439 ["British Virgin Islands", "vg", "1284"],
38440 ["Brunei", "bn", "673"],
38441 ["Bulgaria (България)", "bg", "359"],
38442 ["Burkina Faso", "bf", "226"],
38443 ["Burundi (Uburundi)", "bi", "257"],
38444 ["Cambodia (កម្ពុជា)", "kh", "855"],
38445 ["Cameroon (Cameroun)", "cm", "237"],
38446 ["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"]],
38447 ["Cape Verde (Kabu Verdi)", "cv", "238"],
38448 ["Caribbean Netherlands", "bq", "599", 1],
38449 ["Cayman Islands", "ky", "1345"],
38450 ["Central African Republic (République centrafricaine)", "cf", "236"],
38451 ["Chad (Tchad)", "td", "235"],
38452 ["Chile", "cl", "56"],
38453 ["China (中国)", "cn", "86"],
38454 ["Christmas Island", "cx", "61", 2],
38455 ["Cocos (Keeling) Islands", "cc", "61", 1],
38456 ["Colombia", "co", "57"],
38457 ["Comoros (جزر القمر)", "km", "269"],
38458 ["Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)", "cd", "243"],
38459 ["Congo (Republic) (Congo-Brazzaville)", "cg", "242"],
38460 ["Cook Islands", "ck", "682"],
38461 ["Costa Rica", "cr", "506"],
38462 ["Côte d’Ivoire", "ci", "225"],
38463 ["Croatia (Hrvatska)", "hr", "385"],
38464 ["Cuba", "cu", "53"],
38465 ["Curaçao", "cw", "599", 0],
38466 ["Cyprus (Κύπρος)", "cy", "357"],
38467 ["Czech Republic (Česká republika)", "cz", "420"],
38468 ["Denmark (Danmark)", "dk", "45"],
38469 ["Djibouti", "dj", "253"],
38470 ["Dominica", "dm", "1767"],
38471 ["Dominican Republic (República Dominicana)", "do", "1", 2, ["809", "829", "849"]],
38472 ["Ecuador", "ec", "593"],
38473 ["Egypt (مصر)", "eg", "20"],
38474 ["El Salvador", "sv", "503"],
38475 ["Equatorial Guinea (Guinea Ecuatorial)", "gq", "240"],
38476 ["Eritrea", "er", "291"],
38477 ["Estonia (Eesti)", "ee", "372"],
38478 ["Ethiopia", "et", "251"],
38479 ["Falkland Islands (Islas Malvinas)", "fk", "500"],
38480 ["Faroe Islands (Føroyar)", "fo", "298"],
38481 ["Fiji", "fj", "679"],
38482 ["Finland (Suomi)", "fi", "358", 0],
38483 ["France", "fr", "33"],
38484 ["French Guiana (Guyane française)", "gf", "594"],
38485 ["French Polynesia (Polynésie française)", "pf", "689"],
38486 ["Gabon", "ga", "241"],
38487 ["Gambia", "gm", "220"],
38488 ["Georgia (საქართველო)", "ge", "995"],
38489 ["Germany (Deutschland)", "de", "49"],
38490 ["Ghana (Gaana)", "gh", "233"],
38491 ["Gibraltar", "gi", "350"],
38492 ["Greece (Ελλάδα)", "gr", "30"],
38493 ["Greenland (Kalaallit Nunaat)", "gl", "299"],
38494 ["Grenada", "gd", "1473"],
38495 ["Guadeloupe", "gp", "590", 0],
38496 ["Guam", "gu", "1671"],
38497 ["Guatemala", "gt", "502"],
38498 ["Guernsey", "gg", "44", 1],
38499 ["Guinea (Guinée)", "gn", "224"],
38500 ["Guinea-Bissau (Guiné Bissau)", "gw", "245"],
38501 ["Guyana", "gy", "592"],
38502 ["Haiti", "ht", "509"],
38503 ["Honduras", "hn", "504"],
38504 ["Hong Kong (香港)", "hk", "852"],
38505 ["Hungary (Magyarország)", "hu", "36"],
38506 ["Iceland (Ísland)", "is", "354"],
38507 ["India (भारत)", "in", "91"],
38508 ["Indonesia", "id", "62"],
38509 ["Iran (ایران)", "ir", "98"],
38510 ["Iraq (العراق)", "iq", "964"],
38511 ["Ireland", "ie", "353"],
38512 ["Isle of Man", "im", "44", 2],
38513 ["Israel (ישראל)", "il", "972"],
38514 ["Italy (Italia)", "it", "39", 0],
38515 ["Jamaica", "jm", "1876"],
38516 ["Japan (日本)", "jp", "81"],
38517 ["Jersey", "je", "44", 3],
38518 ["Jordan (الأردن)", "jo", "962"],
38519 ["Kazakhstan (Казахстан)", "kz", "7", 1],
38520 ["Kenya", "ke", "254"],
38521 ["Kiribati", "ki", "686"],
38522 ["Kosovo", "xk", "383"],
38523 ["Kuwait (الكويت)", "kw", "965"],
38524 ["Kyrgyzstan (Кыргызстан)", "kg", "996"],
38525 ["Laos (ລາວ)", "la", "856"],
38526 ["Latvia (Latvija)", "lv", "371"],
38527 ["Lebanon (لبنان)", "lb", "961"],
38528 ["Lesotho", "ls", "266"],
38529 ["Liberia", "lr", "231"],
38530 ["Libya (ليبيا)", "ly", "218"],
38531 ["Liechtenstein", "li", "423"],
38532 ["Lithuania (Lietuva)", "lt", "370"],
38533 ["Luxembourg", "lu", "352"],
38534 ["Macau (澳門)", "mo", "853"],
38535 ["Macedonia (FYROM) (Македонија)", "mk", "389"],
38536 ["Madagascar (Madagasikara)", "mg", "261"],
38537 ["Malawi", "mw", "265"],
38538 ["Malaysia", "my", "60"],
38539 ["Maldives", "mv", "960"],
38540 ["Mali", "ml", "223"],
38541 ["Malta", "mt", "356"],
38542 ["Marshall Islands", "mh", "692"],
38543 ["Martinique", "mq", "596"],
38544 ["Mauritania (موريتانيا)", "mr", "222"],
38545 ["Mauritius (Moris)", "mu", "230"],
38546 ["Mayotte", "yt", "262", 1],
38547 ["Mexico (México)", "mx", "52"],
38548 ["Micronesia", "fm", "691"],
38549 ["Moldova (Republica Moldova)", "md", "373"],
38550 ["Monaco", "mc", "377"],
38551 ["Mongolia (Монгол)", "mn", "976"],
38552 ["Montenegro (Crna Gora)", "me", "382"],
38553 ["Montserrat", "ms", "1664"],
38554 ["Morocco (المغرب)", "ma", "212", 0],
38555 ["Mozambique (Moçambique)", "mz", "258"],
38556 ["Myanmar (Burma) (မြန်မာ)", "mm", "95"],
38557 ["Namibia (Namibië)", "na", "264"],
38558 ["Nauru", "nr", "674"],
38559 ["Nepal (नेपाल)", "np", "977"],
38560 ["Netherlands (Nederland)", "nl", "31"],
38561 ["New Caledonia (Nouvelle-Calédonie)", "nc", "687"],
38562 ["New Zealand", "nz", "64"],
38563 ["Nicaragua", "ni", "505"],
38564 ["Niger (Nijar)", "ne", "227"],
38565 ["Nigeria", "ng", "234"],
38566 ["Niue", "nu", "683"],
38567 ["Norfolk Island", "nf", "672"],
38568 ["North Korea (조선 민주주의 인민 공화국)", "kp", "850"],
38569 ["Northern Mariana Islands", "mp", "1670"],
38570 ["Norway (Norge)", "no", "47", 0],
38571 ["Oman (عُمان)", "om", "968"],
38572 ["Pakistan (پاکستان)", "pk", "92"],
38573 ["Palau", "pw", "680"],
38574 ["Palestine (فلسطين)", "ps", "970"],
38575 ["Panama (Panamá)", "pa", "507"],
38576 ["Papua New Guinea", "pg", "675"],
38577 ["Paraguay", "py", "595"],
38578 ["Peru (Perú)", "pe", "51"],
38579 ["Philippines", "ph", "63"],
38580 ["Poland (Polska)", "pl", "48"],
38581 ["Portugal", "pt", "351"],
38582 ["Puerto Rico", "pr", "1", 3, ["787", "939"]],
38583 ["Qatar (قطر)", "qa", "974"],
38584 ["Réunion (La Réunion)", "re", "262", 0],
38585 ["Romania (România)", "ro", "40"],
38586 ["Russia (Россия)", "ru", "7", 0],
38587 ["Rwanda", "rw", "250"],
38588 ["Saint Barthélemy", "bl", "590", 1],
38589 ["Saint Helena", "sh", "290"],
38590 ["Saint Kitts and Nevis", "kn", "1869"],
38591 ["Saint Lucia", "lc", "1758"],
38592 ["Saint Martin (Saint-Martin (partie française))", "mf", "590", 2],
38593 ["Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)", "pm", "508"],
38594 ["Saint Vincent and the Grenadines", "vc", "1784"],
38595 ["Samoa", "ws", "685"],
38596 ["San Marino", "sm", "378"],
38597 ["São Tomé and Príncipe (São Tomé e Príncipe)", "st", "239"],
38598 ["Saudi Arabia (المملكة العربية السعودية)", "sa", "966"],
38599 ["Senegal (Sénégal)", "sn", "221"],
38600 ["Serbia (Србија)", "rs", "381"],
38601 ["Seychelles", "sc", "248"],
38602 ["Sierra Leone", "sl", "232"],
38603 ["Singapore", "sg", "65"],
38604 ["Sint Maarten", "sx", "1721"],
38605 ["Slovakia (Slovensko)", "sk", "421"],
38606 ["Slovenia (Slovenija)", "si", "386"],
38607 ["Solomon Islands", "sb", "677"],
38608 ["Somalia (Soomaaliya)", "so", "252"],
38609 ["South Africa", "za", "27"],
38610 ["South Korea (대한민국)", "kr", "82"],
38611 ["South Sudan (جنوب السودان)", "ss", "211"],
38612 ["Spain (España)", "es", "34"],
38613 ["Sri Lanka (ශ්රී ලංකාව)", "lk", "94"],
38614 ["Sudan (السودان)", "sd", "249"],
38615 ["Suriname", "sr", "597"],
38616 ["Svalbard and Jan Mayen", "sj", "47", 1],
38617 ["Swaziland", "sz", "268"],
38618 ["Sweden (Sverige)", "se", "46"],
38619 ["Switzerland (Schweiz)", "ch", "41"],
38620 ["Syria (سوريا)", "sy", "963"],
38621 ["Taiwan (台灣)", "tw", "886"],
38622 ["Tajikistan", "tj", "992"],
38623 ["Tanzania", "tz", "255"],
38624 ["Thailand (ไทย)", "th", "66"],
38625 ["Timor-Leste", "tl", "670"],
38626 ["Togo", "tg", "228"],
38627 ["Tokelau", "tk", "690"],
38628 ["Tonga", "to", "676"],
38629 ["Trinidad and Tobago", "tt", "1868"],
38630 ["Tunisia (تونس)", "tn", "216"],
38631 ["Turkey (Türkiye)", "tr", "90"],
38632 ["Turkmenistan", "tm", "993"],
38633 ["Turks and Caicos Islands", "tc", "1649"],
38634 ["Tuvalu", "tv", "688"],
38635 ["U.S. Virgin Islands", "vi", "1340"],
38636 ["Uganda", "ug", "256"],
38637 ["Ukraine (Україна)", "ua", "380"],
38638 ["United Arab Emirates (الإمارات العربية المتحدة)", "ae", "971"],
38639 ["United Kingdom", "gb", "44", 0],
38640 ["United States", "us", "1", 0],
38641 ["Uruguay", "uy", "598"],
38642 ["Uzbekistan (Oʻzbekiston)", "uz", "998"],
38643 ["Vanuatu", "vu", "678"],
38644 ["Vatican City (Città del Vaticano)", "va", "39", 1],
38645 ["Venezuela", "ve", "58"],
38646 ["Vietnam (Việt Nam)", "vn", "84"],
38647 ["Wallis and Futuna (Wallis-et-Futuna)", "wf", "681"],
38648 ["Western Sahara (الصحراء الغربية)", "eh", "212", 1],
38649 ["Yemen (اليمن)", "ye", "967"],
38650 ["Zambia", "zm", "260"],
38651 ["Zimbabwe", "zw", "263"],
38652 ["Åland Islands", "ax", "358", 1]