2 * set the version of bootstrap based on the stylesheet...
6 Roo.bootstrap.version = (
9 Roo.each(document.styleSheets[0], function(s) {
10 if (s.href.match(/css-bootstrap4/)) {
18 * base class for bootstrap elements.
22 Roo.bootstrap = Roo.bootstrap || {};
24 * @class Roo.bootstrap.Component
25 * @extends Roo.Component
26 * Bootstrap Component base class
27 * @cfg {String} cls css class
28 * @cfg {String} style any extra css
29 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
30 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
31 * @cfg {string} dataId cutomer id
32 * @cfg {string} name Specifies name attribute
33 * @cfg {string} tooltip Text for the tooltip
34 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
35 * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
38 * Do not use directly - it does not do anything..
39 * @param {Object} config The config object
44 Roo.bootstrap.Component = function(config){
45 Roo.bootstrap.Component.superclass.constructor.call(this, config);
49 * @event childrenrendered
50 * Fires when the children have been rendered..
51 * @param {Roo.bootstrap.Component} this
53 "childrenrendered" : true
62 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
65 allowDomMove : false, // to stop relocations in parent onRender...
75 * Initialize Events for the element
77 initEvents : function() { },
83 can_build_overlaid : true,
85 container_method : false,
92 // returns the parent component..
93 return Roo.ComponentMgr.get(this.parentId)
99 onRender : function(ct, position)
101 // Roo.log("Call onRender: " + this.xtype);
103 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
106 if (this.el.attr('xtype')) {
107 this.el.attr('xtypex', this.el.attr('xtype'));
108 this.el.dom.removeAttribute('xtype');
118 var cfg = Roo.apply({}, this.getAutoCreate());
120 cfg.id = this.id || Roo.id();
122 // fill in the extra attributes
123 if (this.xattr && typeof(this.xattr) =='object') {
124 for (var i in this.xattr) {
125 cfg[i] = this.xattr[i];
130 cfg.dataId = this.dataId;
134 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
137 if (this.style) { // fixme needs to support more complex style data.
138 cfg.style = this.style;
142 cfg.name = this.name;
145 this.el = ct.createChild(cfg, position);
148 this.tooltipEl().attr('tooltip', this.tooltip);
151 if(this.tabIndex !== undefined){
152 this.el.dom.setAttribute('tabIndex', this.tabIndex);
159 * Fetch the element to add children to
160 * @return {Roo.Element} defaults to this.el
162 getChildContainer : function()
167 * Fetch the element to display the tooltip on.
168 * @return {Roo.Element} defaults to this.el
170 tooltipEl : function()
175 addxtype : function(tree,cntr)
179 cn = Roo.factory(tree);
180 //Roo.log(['addxtype', cn]);
182 cn.parentType = this.xtype; //??
183 cn.parentId = this.id;
185 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
186 if (typeof(cn.container_method) == 'string') {
187 cntr = cn.container_method;
191 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
193 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
195 var build_from_html = Roo.XComponent.build_from_html;
197 var is_body = (tree.xtype == 'Body') ;
199 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
201 var self_cntr_el = Roo.get(this[cntr](false));
203 // do not try and build conditional elements
204 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
208 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
209 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
210 return this.addxtypeChild(tree,cntr, is_body);
213 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
216 return this.addxtypeChild(Roo.apply({}, tree),cntr);
219 Roo.log('skipping render');
225 if (!build_from_html) {
229 // this i think handles overlaying multiple children of the same type
230 // with the sam eelement.. - which might be buggy..
232 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
238 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
242 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
249 addxtypeChild : function (tree, cntr, is_body)
251 Roo.debug && Roo.log('addxtypeChild:' + cntr);
253 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
256 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
257 (typeof(tree['flexy:foreach']) != 'undefined');
261 skip_children = false;
262 // render the element if it's not BODY.
265 // if parent was disabled, then do not try and create the children..
266 if(!this[cntr](true)){
271 cn = Roo.factory(tree);
273 cn.parentType = this.xtype; //??
274 cn.parentId = this.id;
276 var build_from_html = Roo.XComponent.build_from_html;
279 // does the container contain child eleemnts with 'xtype' attributes.
280 // that match this xtype..
281 // note - when we render we create these as well..
282 // so we should check to see if body has xtype set.
283 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
285 var self_cntr_el = Roo.get(this[cntr](false));
286 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
288 //Roo.log(Roo.XComponent.build_from_html);
289 //Roo.log("got echild:");
292 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
293 // and are not displayed -this causes this to use up the wrong element when matching.
294 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
297 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
298 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
304 //echild.dom.removeAttribute('xtype');
306 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
307 Roo.debug && Roo.log(self_cntr_el);
308 Roo.debug && Roo.log(echild);
309 Roo.debug && Roo.log(cn);
315 // if object has flexy:if - then it may or may not be rendered.
316 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
317 // skip a flexy if element.
318 Roo.debug && Roo.log('skipping render');
319 Roo.debug && Roo.log(tree);
321 Roo.debug && Roo.log('skipping all children');
322 skip_children = true;
327 // actually if flexy:foreach is found, we really want to create
328 // multiple copies here...
330 //Roo.log(this[cntr]());
331 // some elements do not have render methods.. like the layouts...
333 if(this[cntr](true) === false){
338 cn.render && cn.render(this[cntr](true));
341 // then add the element..
348 if (typeof (tree.menu) != 'undefined') {
349 tree.menu.parentType = cn.xtype;
350 tree.menu.triggerEl = cn.el;
351 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
355 if (!tree.items || !tree.items.length) {
357 //Roo.log(["no children", this]);
362 var items = tree.items;
365 //Roo.log(items.length);
367 if (!skip_children) {
368 for(var i =0;i < items.length;i++) {
369 // Roo.log(['add child', items[i]]);
370 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
376 //Roo.log("fire childrenrendered");
378 cn.fireEvent('childrenrendered', this);
384 * Set the element that will be used to show or hide
386 setVisibilityEl : function(el)
388 this.visibilityEl = el;
392 * Get the element that will be used to show or hide
394 getVisibilityEl : function()
396 if (typeof(this.visibilityEl) == 'object') {
397 return this.visibilityEl;
400 if (typeof(this.visibilityEl) == 'string') {
401 return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
408 * Show a component - removes 'hidden' class
412 if(!this.getVisibilityEl()){
416 this.getVisibilityEl().removeClass('hidden');
418 this.fireEvent('show', this);
423 * Hide a component - adds 'hidden' class
427 if(!this.getVisibilityEl()){
431 this.getVisibilityEl().addClass('hidden');
433 this.fireEvent('hide', this);
446 * @class Roo.bootstrap.Body
447 * @extends Roo.bootstrap.Component
448 * Bootstrap Body class
452 * @param {Object} config The config object
455 Roo.bootstrap.Body = function(config){
457 config = config || {};
459 Roo.bootstrap.Body.superclass.constructor.call(this, config);
460 this.el = Roo.get(config.el ? config.el : document.body );
461 if (this.cls && this.cls.length) {
462 Roo.get(document.body).addClass(this.cls);
466 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
468 is_body : true,// just to make sure it's constructed?
473 onRender : function(ct, position)
475 /* Roo.log("Roo.bootstrap.Body - onRender");
476 if (this.cls && this.cls.length) {
477 Roo.get(document.body).addClass(this.cls);
496 * @class Roo.bootstrap.ButtonGroup
497 * @extends Roo.bootstrap.Component
498 * Bootstrap ButtonGroup class
499 * @cfg {String} size lg | sm | xs (default empty normal)
500 * @cfg {String} align vertical | justified (default none)
501 * @cfg {String} direction up | down (default down)
502 * @cfg {Boolean} toolbar false | true
503 * @cfg {Boolean} btn true | false
508 * @param {Object} config The config object
511 Roo.bootstrap.ButtonGroup = function(config){
512 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
515 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
523 getAutoCreate : function(){
529 cfg.html = this.html || cfg.html;
540 if (['vertical','justified'].indexOf(this.align)!==-1) {
541 cfg.cls = 'btn-group-' + this.align;
543 if (this.align == 'justified') {
544 console.log(this.items);
548 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
549 cfg.cls += ' btn-group-' + this.size;
552 if (this.direction == 'up') {
553 cfg.cls += ' dropup' ;
569 * @class Roo.bootstrap.Button
570 * @extends Roo.bootstrap.Component
571 * Bootstrap Button class
572 * @cfg {String} html The button content
573 * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default
574 * @cfg {String} size ( lg | sm | xs)
575 * @cfg {String} tag ( a | input | submit)
576 * @cfg {String} href empty or href
577 * @cfg {Boolean} disabled default false;
578 * @cfg {Boolean} isClose default false;
579 * @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)
580 * @cfg {String} badge text for badge
581 * @cfg {String} theme (default|glow)
582 * @cfg {Boolean} inverse dark themed version
583 * @cfg {Boolean} toggle is it a slidy toggle button
584 * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
585 * @cfg {String} ontext text for on slidy toggle state
586 * @cfg {String} offtext text for off slidy toggle state
587 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
588 * @cfg {Boolean} removeClass remove the standard class..
589 * @cfg {String} target target for a href. (_self|_blank|_parent|_top| other)
592 * Create a new button
593 * @param {Object} config The config object
597 Roo.bootstrap.Button = function(config){
598 Roo.bootstrap.Button.superclass.constructor.call(this, config);
599 this.weightClass = ["btn-default",
611 * When a butotn is pressed
612 * @param {Roo.bootstrap.Button} btn
613 * @param {Roo.EventObject} e
618 * After the button has been toggles
619 * @param {Roo.bootstrap.Button} btn
620 * @param {Roo.EventObject} e
621 * @param {boolean} pressed (also available as button.pressed)
627 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
645 preventDefault: true,
653 getAutoCreate : function(){
661 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
662 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
667 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
669 if (this.toggle == true) {
672 cls: 'slider-frame roo-button',
677 'data-off-text':'OFF',
678 cls: 'slider-button',
684 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
685 cfg.cls += ' '+this.weight;
694 cfg["aria-hidden"] = true;
696 cfg.html = "×";
702 if (this.theme==='default') {
703 cfg.cls = 'btn roo-button';
705 //if (this.parentType != 'Navbar') {
706 this.weight = this.weight.length ? this.weight : 'default';
708 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
710 cfg.cls += ' btn-' + this.weight;
712 } else if (this.theme==='glow') {
715 cfg.cls = 'btn-glow roo-button';
717 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
719 cfg.cls += ' ' + this.weight;
725 this.cls += ' inverse';
729 if (this.active || this.pressed === true) {
730 cfg.cls += ' active';
734 cfg.disabled = 'disabled';
738 Roo.log('changing to ul' );
740 this.glyphicon = 'caret';
743 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
745 //gsRoo.log(this.parentType);
746 if (this.parentType === 'Navbar' && !this.parent().bar) {
747 Roo.log('changing to li?');
756 href : this.href || '#'
759 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
760 cfg.cls += ' dropdown';
767 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
769 if (this.glyphicon) {
770 cfg.html = ' ' + cfg.html;
775 cls: 'glyphicon glyphicon-' + this.glyphicon
785 // cfg.cls='btn roo-button';
789 var value = cfg.html;
794 cls: 'glyphicon glyphicon-' + this.glyphicon,
813 cfg.cls += ' dropdown';
814 cfg.html = typeof(cfg.html) != 'undefined' ?
815 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
818 if (cfg.tag !== 'a' && this.href !== '') {
819 throw "Tag must be a to set href.";
820 } else if (this.href.length > 0) {
821 cfg.href = this.href;
824 if(this.removeClass){
829 cfg.target = this.target;
834 initEvents: function() {
835 // Roo.log('init events?');
836 // Roo.log(this.el.dom);
839 if (typeof (this.menu) != 'undefined') {
840 this.menu.parentType = this.xtype;
841 this.menu.triggerEl = this.el;
842 this.addxtype(Roo.apply({}, this.menu));
846 if (this.el.hasClass('roo-button')) {
847 this.el.on('click', this.onClick, this);
849 this.el.select('.roo-button').on('click', this.onClick, this);
852 if(this.removeClass){
853 this.el.on('click', this.onClick, this);
856 this.el.enableDisplayMode();
859 onClick : function(e)
865 Roo.log('button on click ');
866 if(this.preventDefault){
870 if (this.pressed === true || this.pressed === false) {
871 this.toggleActive(e);
875 this.fireEvent('click', this, e);
879 * Enables this button
883 this.disabled = false;
884 this.el.removeClass('disabled');
888 * Disable this button
892 this.disabled = true;
893 this.el.addClass('disabled');
896 * sets the active state on/off,
897 * @param {Boolean} state (optional) Force a particular state
899 setActive : function(v) {
901 this.el[v ? 'addClass' : 'removeClass']('active');
905 * toggles the current active state
907 toggleActive : function(e)
909 this.setActive(!this.pressed);
910 this.fireEvent('toggle', this, e, !this.pressed);
913 * get the current active state
914 * @return {boolean} true if it's active
916 isActive : function()
918 return this.el.hasClass('active');
921 * set the text of the first selected button
923 setText : function(str)
925 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
928 * get the text of the first selected button
932 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
935 setWeight : function(str)
937 this.el.removeClass(this.weightClass);
938 this.el.addClass('btn-' + str);
952 * @class Roo.bootstrap.Column
953 * @extends Roo.bootstrap.Component
954 * Bootstrap Column class
955 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
956 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
957 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
958 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
959 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
960 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
961 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
962 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
965 * @cfg {Boolean} hidden (true|false) hide the element
966 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
967 * @cfg {String} fa (ban|check|...) font awesome icon
968 * @cfg {Number} fasize (1|2|....) font awsome size
970 * @cfg {String} icon (info-sign|check|...) glyphicon name
972 * @cfg {String} html content of column.
975 * Create a new Column
976 * @param {Object} config The config object
979 Roo.bootstrap.Column = function(config){
980 Roo.bootstrap.Column.superclass.constructor.call(this, config);
983 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
1001 getAutoCreate : function(){
1002 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1010 ['xs','sm','md','lg'].map(function(size){
1011 //Roo.log( size + ':' + settings[size]);
1013 if (settings[size+'off'] !== false) {
1014 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1017 if (settings[size] === false) {
1021 if (!settings[size]) { // 0 = hidden
1022 cfg.cls += ' hidden-' + size;
1025 cfg.cls += ' col-' + size + '-' + settings[size];
1030 cfg.cls += ' hidden';
1033 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1034 cfg.cls +=' alert alert-' + this.alert;
1038 if (this.html.length) {
1039 cfg.html = this.html;
1043 if (this.fasize > 1) {
1044 fasize = ' fa-' + this.fasize + 'x';
1046 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1051 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1070 * @class Roo.bootstrap.Container
1071 * @extends Roo.bootstrap.Component
1072 * Bootstrap Container class
1073 * @cfg {Boolean} jumbotron is it a jumbotron element
1074 * @cfg {String} html content of element
1075 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1076 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1077 * @cfg {String} header content of header (for panel)
1078 * @cfg {String} footer content of footer (for panel)
1079 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1080 * @cfg {String} tag (header|aside|section) type of HTML tag.
1081 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1082 * @cfg {String} fa font awesome icon
1083 * @cfg {String} icon (info-sign|check|...) glyphicon name
1084 * @cfg {Boolean} hidden (true|false) hide the element
1085 * @cfg {Boolean} expandable (true|false) default false
1086 * @cfg {Boolean} expanded (true|false) default true
1087 * @cfg {String} rheader contet on the right of header
1088 * @cfg {Boolean} clickable (true|false) default false
1092 * Create a new Container
1093 * @param {Object} config The config object
1096 Roo.bootstrap.Container = function(config){
1097 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1103 * After the panel has been expand
1105 * @param {Roo.bootstrap.Container} this
1110 * After the panel has been collapsed
1112 * @param {Roo.bootstrap.Container} this
1117 * When a element is chick
1118 * @param {Roo.bootstrap.Container} this
1119 * @param {Roo.EventObject} e
1125 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1143 getChildContainer : function() {
1149 if (this.panel.length) {
1150 return this.el.select('.panel-body',true).first();
1157 getAutoCreate : function(){
1160 tag : this.tag || 'div',
1164 if (this.jumbotron) {
1165 cfg.cls = 'jumbotron';
1170 // - this is applied by the parent..
1172 // cfg.cls = this.cls + '';
1175 if (this.sticky.length) {
1177 var bd = Roo.get(document.body);
1178 if (!bd.hasClass('bootstrap-sticky')) {
1179 bd.addClass('bootstrap-sticky');
1180 Roo.select('html',true).setStyle('height', '100%');
1183 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1187 if (this.well.length) {
1188 switch (this.well) {
1191 cfg.cls +=' well well-' +this.well;
1200 cfg.cls += ' hidden';
1204 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1205 cfg.cls +=' alert alert-' + this.alert;
1210 if (this.panel.length) {
1211 cfg.cls += ' panel panel-' + this.panel;
1213 if (this.header.length) {
1217 if(this.expandable){
1219 cfg.cls = cfg.cls + ' expandable';
1223 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1231 cls : 'panel-title',
1232 html : (this.expandable ? ' ' : '') + this.header
1236 cls: 'panel-header-right',
1242 cls : 'panel-heading',
1243 style : this.expandable ? 'cursor: pointer' : '',
1251 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1256 if (this.footer.length) {
1258 cls : 'panel-footer',
1267 body.html = this.html || cfg.html;
1268 // prefix with the icons..
1270 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1273 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1278 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1279 cfg.cls = 'container';
1285 initEvents: function()
1287 if(this.expandable){
1288 var headerEl = this.headerEl();
1291 headerEl.on('click', this.onToggleClick, this);
1296 this.el.on('click', this.onClick, this);
1301 onToggleClick : function()
1303 var headerEl = this.headerEl();
1319 if(this.fireEvent('expand', this)) {
1321 this.expanded = true;
1323 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1325 this.el.select('.panel-body',true).first().removeClass('hide');
1327 var toggleEl = this.toggleEl();
1333 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1338 collapse : function()
1340 if(this.fireEvent('collapse', this)) {
1342 this.expanded = false;
1344 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1345 this.el.select('.panel-body',true).first().addClass('hide');
1347 var toggleEl = this.toggleEl();
1353 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1357 toggleEl : function()
1359 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1363 return this.el.select('.panel-heading .fa',true).first();
1366 headerEl : function()
1368 if(!this.el || !this.panel.length || !this.header.length){
1372 return this.el.select('.panel-heading',true).first()
1377 if(!this.el || !this.panel.length){
1381 return this.el.select('.panel-body',true).first()
1384 titleEl : function()
1386 if(!this.el || !this.panel.length || !this.header.length){
1390 return this.el.select('.panel-title',true).first();
1393 setTitle : function(v)
1395 var titleEl = this.titleEl();
1401 titleEl.dom.innerHTML = v;
1404 getTitle : function()
1407 var titleEl = this.titleEl();
1413 return titleEl.dom.innerHTML;
1416 setRightTitle : function(v)
1418 var t = this.el.select('.panel-header-right',true).first();
1424 t.dom.innerHTML = v;
1427 onClick : function(e)
1431 this.fireEvent('click', this, e);
1444 * @class Roo.bootstrap.Img
1445 * @extends Roo.bootstrap.Component
1446 * Bootstrap Img class
1447 * @cfg {Boolean} imgResponsive false | true
1448 * @cfg {String} border rounded | circle | thumbnail
1449 * @cfg {String} src image source
1450 * @cfg {String} alt image alternative text
1451 * @cfg {String} href a tag href
1452 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1453 * @cfg {String} xsUrl xs image source
1454 * @cfg {String} smUrl sm image source
1455 * @cfg {String} mdUrl md image source
1456 * @cfg {String} lgUrl lg image source
1459 * Create a new Input
1460 * @param {Object} config The config object
1463 Roo.bootstrap.Img = function(config){
1464 Roo.bootstrap.Img.superclass.constructor.call(this, config);
1470 * The img click event for the img.
1471 * @param {Roo.EventObject} e
1477 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1479 imgResponsive: true,
1489 getAutoCreate : function()
1491 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1492 return this.createSingleImg();
1497 cls: 'roo-image-responsive-group',
1502 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1504 if(!_this[size + 'Url']){
1510 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1511 html: _this.html || cfg.html,
1512 src: _this[size + 'Url']
1515 img.cls += ' roo-image-responsive-' + size;
1517 var s = ['xs', 'sm', 'md', 'lg'];
1519 s.splice(s.indexOf(size), 1);
1521 Roo.each(s, function(ss){
1522 img.cls += ' hidden-' + ss;
1525 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1526 cfg.cls += ' img-' + _this.border;
1530 cfg.alt = _this.alt;
1543 a.target = _this.target;
1547 cfg.cn.push((_this.href) ? a : img);
1554 createSingleImg : function()
1558 cls: (this.imgResponsive) ? 'img-responsive' : '',
1560 src : 'about:blank' // just incase src get's set to undefined?!?
1563 cfg.html = this.html || cfg.html;
1565 cfg.src = this.src || cfg.src;
1567 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1568 cfg.cls += ' img-' + this.border;
1585 a.target = this.target;
1590 return (this.href) ? a : cfg;
1593 initEvents: function()
1596 this.el.on('click', this.onClick, this);
1601 onClick : function(e)
1603 Roo.log('img onclick');
1604 this.fireEvent('click', this, e);
1607 * Sets the url of the image - used to update it
1608 * @param {String} url the url of the image
1611 setSrc : function(url)
1615 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1616 this.el.dom.src = url;
1620 this.el.select('img', true).first().dom.src = url;
1636 * @class Roo.bootstrap.Link
1637 * @extends Roo.bootstrap.Component
1638 * Bootstrap Link Class
1639 * @cfg {String} alt image alternative text
1640 * @cfg {String} href a tag href
1641 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1642 * @cfg {String} html the content of the link.
1643 * @cfg {String} anchor name for the anchor link
1644 * @cfg {String} fa - favicon
1646 * @cfg {Boolean} preventDefault (true | false) default false
1650 * Create a new Input
1651 * @param {Object} config The config object
1654 Roo.bootstrap.Link = function(config){
1655 Roo.bootstrap.Link.superclass.constructor.call(this, config);
1661 * The img click event for the img.
1662 * @param {Roo.EventObject} e
1668 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
1672 preventDefault: false,
1678 getAutoCreate : function()
1680 var html = this.html || '';
1682 if (this.fa !== false) {
1683 html = '<i class="fa fa-' + this.fa + '"></i>';
1688 // anchor's do not require html/href...
1689 if (this.anchor === false) {
1691 cfg.href = this.href || '#';
1693 cfg.name = this.anchor;
1694 if (this.html !== false || this.fa !== false) {
1697 if (this.href !== false) {
1698 cfg.href = this.href;
1702 if(this.alt !== false){
1707 if(this.target !== false) {
1708 cfg.target = this.target;
1714 initEvents: function() {
1716 if(!this.href || this.preventDefault){
1717 this.el.on('click', this.onClick, this);
1721 onClick : function(e)
1723 if(this.preventDefault){
1726 //Roo.log('img onclick');
1727 this.fireEvent('click', this, e);
1740 * @class Roo.bootstrap.Header
1741 * @extends Roo.bootstrap.Component
1742 * Bootstrap Header class
1743 * @cfg {String} html content of header
1744 * @cfg {Number} level (1|2|3|4|5|6) default 1
1747 * Create a new Header
1748 * @param {Object} config The config object
1752 Roo.bootstrap.Header = function(config){
1753 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1756 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1764 getAutoCreate : function(){
1769 tag: 'h' + (1 *this.level),
1770 html: this.html || ''
1782 * Ext JS Library 1.1.1
1783 * Copyright(c) 2006-2007, Ext JS, LLC.
1785 * Originally Released Under LGPL - original licence link has changed is not relivant.
1788 * <script type="text/javascript">
1792 * @class Roo.bootstrap.MenuMgr
1793 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1796 Roo.bootstrap.MenuMgr = function(){
1797 var menus, active, groups = {}, attached = false, lastShow = new Date();
1799 // private - called when first menu is created
1802 active = new Roo.util.MixedCollection();
1803 Roo.get(document).addKeyListener(27, function(){
1804 if(active.length > 0){
1812 if(active && active.length > 0){
1813 var c = active.clone();
1823 if(active.length < 1){
1824 Roo.get(document).un("mouseup", onMouseDown);
1832 var last = active.last();
1833 lastShow = new Date();
1836 Roo.get(document).on("mouseup", onMouseDown);
1841 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1842 m.parentMenu.activeChild = m;
1843 }else if(last && last.isVisible()){
1844 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1849 function onBeforeHide(m){
1851 m.activeChild.hide();
1853 if(m.autoHideTimer){
1854 clearTimeout(m.autoHideTimer);
1855 delete m.autoHideTimer;
1860 function onBeforeShow(m){
1861 var pm = m.parentMenu;
1862 if(!pm && !m.allowOtherMenus){
1864 }else if(pm && pm.activeChild && active != m){
1865 pm.activeChild.hide();
1869 // private this should really trigger on mouseup..
1870 function onMouseDown(e){
1871 Roo.log("on Mouse Up");
1873 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1874 Roo.log("MenuManager hideAll");
1883 function onBeforeCheck(mi, state){
1885 var g = groups[mi.group];
1886 for(var i = 0, l = g.length; i < l; i++){
1888 g[i].setChecked(false);
1897 * Hides all menus that are currently visible
1899 hideAll : function(){
1904 register : function(menu){
1908 menus[menu.id] = menu;
1909 menu.on("beforehide", onBeforeHide);
1910 menu.on("hide", onHide);
1911 menu.on("beforeshow", onBeforeShow);
1912 menu.on("show", onShow);
1914 if(g && menu.events["checkchange"]){
1918 groups[g].push(menu);
1919 menu.on("checkchange", onCheck);
1924 * Returns a {@link Roo.menu.Menu} object
1925 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1926 * be used to generate and return a new Menu instance.
1928 get : function(menu){
1929 if(typeof menu == "string"){ // menu id
1931 }else if(menu.events){ // menu instance
1934 /*else if(typeof menu.length == 'number'){ // array of menu items?
1935 return new Roo.bootstrap.Menu({items:menu});
1936 }else{ // otherwise, must be a config
1937 return new Roo.bootstrap.Menu(menu);
1944 unregister : function(menu){
1945 delete menus[menu.id];
1946 menu.un("beforehide", onBeforeHide);
1947 menu.un("hide", onHide);
1948 menu.un("beforeshow", onBeforeShow);
1949 menu.un("show", onShow);
1951 if(g && menu.events["checkchange"]){
1952 groups[g].remove(menu);
1953 menu.un("checkchange", onCheck);
1958 registerCheckable : function(menuItem){
1959 var g = menuItem.group;
1964 groups[g].push(menuItem);
1965 menuItem.on("beforecheckchange", onBeforeCheck);
1970 unregisterCheckable : function(menuItem){
1971 var g = menuItem.group;
1973 groups[g].remove(menuItem);
1974 menuItem.un("beforecheckchange", onBeforeCheck);
1986 * @class Roo.bootstrap.Menu
1987 * @extends Roo.bootstrap.Component
1988 * Bootstrap Menu class - container for MenuItems
1989 * @cfg {String} type (dropdown|treeview|submenu) type of menu
1990 * @cfg {bool} hidden if the menu should be hidden when rendered.
1991 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
1992 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
1996 * @param {Object} config The config object
2000 Roo.bootstrap.Menu = function(config){
2001 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
2002 if (this.registerMenu && this.type != 'treeview') {
2003 Roo.bootstrap.MenuMgr.register(this);
2005 this.openClass = Roo.bootstrap.version = 4 ? 'show' : 'open';
2010 * Fires before this menu is displayed
2011 * @param {Roo.menu.Menu} this
2016 * Fires before this menu is hidden
2017 * @param {Roo.menu.Menu} this
2022 * Fires after this menu is displayed
2023 * @param {Roo.menu.Menu} this
2028 * Fires after this menu is hidden
2029 * @param {Roo.menu.Menu} this
2034 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2035 * @param {Roo.menu.Menu} this
2036 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2037 * @param {Roo.EventObject} e
2042 * Fires when the mouse is hovering over this menu
2043 * @param {Roo.menu.Menu} this
2044 * @param {Roo.EventObject} e
2045 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2050 * Fires when the mouse exits this menu
2051 * @param {Roo.menu.Menu} this
2052 * @param {Roo.EventObject} e
2053 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2058 * Fires when a menu item contained in this menu is clicked
2059 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2060 * @param {Roo.EventObject} e
2064 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2067 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
2071 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
2074 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2076 registerMenu : true,
2078 menuItems :false, // stores the menu items..
2088 getChildContainer : function() {
2092 getAutoCreate : function(){
2094 //if (['right'].indexOf(this.align)!==-1) {
2095 // cfg.cn[1].cls += ' pull-right'
2101 cls : 'dropdown-menu' ,
2102 style : 'z-index:1000'
2106 if (this.type === 'submenu') {
2107 cfg.cls = 'submenu active';
2109 if (this.type === 'treeview') {
2110 cfg.cls = 'treeview-menu';
2115 initEvents : function() {
2117 // Roo.log("ADD event");
2118 // Roo.log(this.triggerEl.dom);
2120 this.triggerEl.on('click', this.onTriggerClick, this);
2122 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2125 if (this.triggerEl.hasClass('nav-item')) {
2126 // dropdown toggle on the 'a' in BS4?
2127 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
2129 this.triggerEl.addClass('dropdown-toggle');
2132 this.el.on('touchstart' , this.onTouch, this);
2134 this.el.on('click' , this.onClick, this);
2136 this.el.on("mouseover", this.onMouseOver, this);
2137 this.el.on("mouseout", this.onMouseOut, this);
2141 findTargetItem : function(e)
2143 var t = e.getTarget(".dropdown-menu-item", this.el, true);
2147 //Roo.log(t); Roo.log(t.id);
2149 //Roo.log(this.menuitems);
2150 return this.menuitems.get(t.id);
2152 //return this.items.get(t.menuItemId);
2158 onTouch : function(e)
2160 Roo.log("menu.onTouch");
2161 //e.stopEvent(); this make the user popdown broken
2165 onClick : function(e)
2167 Roo.log("menu.onClick");
2169 var t = this.findTargetItem(e);
2170 if(!t || t.isContainer){
2175 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
2176 if(t == this.activeItem && t.shouldDeactivate(e)){
2177 this.activeItem.deactivate();
2178 delete this.activeItem;
2182 this.setActiveItem(t, true);
2190 Roo.log('pass click event');
2194 this.fireEvent("click", this, t, e);
2198 if(!t.href.length || t.href == '#'){
2199 (function() { _this.hide(); }).defer(100);
2204 onMouseOver : function(e){
2205 var t = this.findTargetItem(e);
2208 // if(t.canActivate && !t.disabled){
2209 // this.setActiveItem(t, true);
2213 this.fireEvent("mouseover", this, e, t);
2215 isVisible : function(){
2216 return !this.hidden;
2218 onMouseOut : function(e){
2219 var t = this.findTargetItem(e);
2222 // if(t == this.activeItem && t.shouldDeactivate(e)){
2223 // this.activeItem.deactivate();
2224 // delete this.activeItem;
2227 this.fireEvent("mouseout", this, e, t);
2232 * Displays this menu relative to another element
2233 * @param {String/HTMLElement/Roo.Element} element The element to align to
2234 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2235 * the element (defaults to this.defaultAlign)
2236 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2238 show : function(el, pos, parentMenu){
2239 this.parentMenu = parentMenu;
2243 this.fireEvent("beforeshow", this);
2244 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2247 * Displays this menu at a specific xy position
2248 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2249 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2251 showAt : function(xy, parentMenu, /* private: */_e){
2252 this.parentMenu = parentMenu;
2257 this.fireEvent("beforeshow", this);
2258 //xy = this.el.adjustForConstraints(xy);
2262 this.hideMenuItems();
2263 this.hidden = false;
2264 this.triggerEl.addClass(this.openClass);
2266 // reassign x when hitting right
2267 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2268 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2271 // reassign y when hitting bottom
2272 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2273 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2276 // but the list may align on trigger left or trigger top... should it be a properity?
2278 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2283 this.fireEvent("show", this);
2289 this.doFocus.defer(50, this);
2293 doFocus : function(){
2295 this.focusEl.focus();
2300 * Hides this menu and optionally all parent menus
2301 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2303 hide : function(deep)
2306 this.hideMenuItems();
2307 if(this.el && this.isVisible()){
2308 this.fireEvent("beforehide", this);
2309 if(this.activeItem){
2310 this.activeItem.deactivate();
2311 this.activeItem = null;
2313 this.triggerEl.removeClass(this.openClass);;
2315 this.fireEvent("hide", this);
2317 if(deep === true && this.parentMenu){
2318 this.parentMenu.hide(true);
2322 onTriggerClick : function(e)
2324 Roo.log('trigger click');
2326 var target = e.getTarget();
2328 Roo.log(target.nodeName.toLowerCase());
2330 if(target.nodeName.toLowerCase() === 'i'){
2336 onTriggerPress : function(e)
2338 Roo.log('trigger press');
2339 //Roo.log(e.getTarget());
2340 // Roo.log(this.triggerEl.dom);
2342 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2343 var pel = Roo.get(e.getTarget());
2344 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2345 Roo.log('is treeview or dropdown?');
2349 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2353 if (this.isVisible()) {
2358 this.show(this.triggerEl, false, false);
2361 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2368 hideMenuItems : function()
2370 Roo.log("hide Menu Items");
2374 //$(backdrop).remove()
2375 this.el.select('.' + this.openClass,true).each(function(aa) {
2377 aa.removeClass(this.openClass);
2378 //var parent = getParent($(this))
2379 //var relatedTarget = { relatedTarget: this }
2381 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2382 //if (e.isDefaultPrevented()) return
2383 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2386 addxtypeChild : function (tree, cntr) {
2387 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2389 this.menuitems.add(comp);
2401 this.getEl().dom.innerHTML = '';
2402 this.menuitems.clear();
2416 * @class Roo.bootstrap.MenuItem
2417 * @extends Roo.bootstrap.Component
2418 * Bootstrap MenuItem class
2419 * @cfg {String} html the menu label
2420 * @cfg {String} href the link
2421 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2422 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2423 * @cfg {Boolean} active used on sidebars to highlight active itesm
2424 * @cfg {String} fa favicon to show on left of menu item.
2425 * @cfg {Roo.bootsrap.Menu} menu the child menu.
2429 * Create a new MenuItem
2430 * @param {Object} config The config object
2434 Roo.bootstrap.MenuItem = function(config){
2435 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2440 * The raw click event for the entire grid.
2441 * @param {Roo.bootstrap.MenuItem} this
2442 * @param {Roo.EventObject} e
2448 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2452 preventDefault: false,
2453 isContainer : false,
2457 getAutoCreate : function(){
2459 if(this.isContainer){
2462 cls: 'dropdown-menu-item dropdown-item'
2476 if (this.fa !== false) {
2479 cls : 'fa fa-' + this.fa
2488 cls: 'dropdown-menu-item dropdown-item',
2491 if (this.parent().type == 'treeview') {
2492 cfg.cls = 'treeview-menu';
2495 cfg.cls += ' active';
2500 anc.href = this.href || cfg.cn[0].href ;
2501 ctag.html = this.html || cfg.cn[0].html ;
2505 initEvents: function()
2507 if (this.parent().type == 'treeview') {
2508 this.el.select('a').on('click', this.onClick, this);
2512 this.menu.parentType = this.xtype;
2513 this.menu.triggerEl = this.el;
2514 this.menu = this.addxtype(Roo.apply({}, this.menu));
2518 onClick : function(e)
2520 Roo.log('item on click ');
2522 if(this.preventDefault){
2525 //this.parent().hideMenuItems();
2527 this.fireEvent('click', this, e);
2546 * @class Roo.bootstrap.MenuSeparator
2547 * @extends Roo.bootstrap.Component
2548 * Bootstrap MenuSeparator class
2551 * Create a new MenuItem
2552 * @param {Object} config The config object
2556 Roo.bootstrap.MenuSeparator = function(config){
2557 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2560 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2562 getAutoCreate : function(){
2581 * @class Roo.bootstrap.Modal
2582 * @extends Roo.bootstrap.Component
2583 * Bootstrap Modal class
2584 * @cfg {String} title Title of dialog
2585 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2586 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2587 * @cfg {Boolean} specificTitle default false
2588 * @cfg {Array} buttons Array of buttons or standard button set..
2589 * @cfg {String} buttonPosition (left|right|center) default right
2590 * @cfg {Boolean} animate default true
2591 * @cfg {Boolean} allow_close default true
2592 * @cfg {Boolean} fitwindow default false
2593 * @cfg {String} size (sm|lg) default empty
2594 * @cfg {Number} max_width set the max width of modal
2598 * Create a new Modal Dialog
2599 * @param {Object} config The config object
2602 Roo.bootstrap.Modal = function(config){
2603 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2608 * The raw btnclick event for the button
2609 * @param {Roo.EventObject} e
2614 * Fire when dialog resize
2615 * @param {Roo.bootstrap.Modal} this
2616 * @param {Roo.EventObject} e
2620 this.buttons = this.buttons || [];
2623 this.tmpl = Roo.factory(this.tmpl);
2628 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2630 title : 'test dialog',
2640 specificTitle: false,
2642 buttonPosition: 'right',
2665 onRender : function(ct, position)
2667 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2670 var cfg = Roo.apply({}, this.getAutoCreate());
2673 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2675 //if (!cfg.name.length) {
2679 cfg.cls += ' ' + this.cls;
2682 cfg.style = this.style;
2684 this.el = Roo.get(document.body).createChild(cfg, position);
2686 //var type = this.el.dom.type;
2689 if(this.tabIndex !== undefined){
2690 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2693 this.dialogEl = this.el.select('.modal-dialog',true).first();
2694 this.bodyEl = this.el.select('.modal-body',true).first();
2695 this.closeEl = this.el.select('.modal-header .close', true).first();
2696 this.headerEl = this.el.select('.modal-header',true).first();
2697 this.titleEl = this.el.select('.modal-title',true).first();
2698 this.footerEl = this.el.select('.modal-footer',true).first();
2700 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2702 //this.el.addClass("x-dlg-modal");
2704 if (this.buttons.length) {
2705 Roo.each(this.buttons, function(bb) {
2706 var b = Roo.apply({}, bb);
2707 b.xns = b.xns || Roo.bootstrap;
2708 b.xtype = b.xtype || 'Button';
2709 if (typeof(b.listeners) == 'undefined') {
2710 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2713 var btn = Roo.factory(b);
2715 btn.render(this.el.select('.modal-footer div').first());
2719 // render the children.
2722 if(typeof(this.items) != 'undefined'){
2723 var items = this.items;
2726 for(var i =0;i < items.length;i++) {
2727 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2731 this.items = nitems;
2733 // where are these used - they used to be body/close/footer
2737 //this.el.addClass([this.fieldClass, this.cls]);
2741 getAutoCreate : function()
2745 html : this.html || ''
2750 cls : 'modal-title',
2754 if(this.specificTitle){
2760 if (this.allow_close) {
2772 if(this.size.length){
2773 size = 'modal-' + this.size;
2780 cls: "modal-dialog " + size,
2783 cls : "modal-content",
2786 cls : 'modal-header',
2791 cls : 'modal-footer',
2795 cls: 'btn-' + this.buttonPosition
2812 modal.cls += ' fade';
2818 getChildContainer : function() {
2823 getButtonContainer : function() {
2824 return this.el.select('.modal-footer div',true).first();
2827 initEvents : function()
2829 if (this.allow_close) {
2830 this.closeEl.on('click', this.hide, this);
2832 Roo.EventManager.onWindowResize(this.resize, this, true);
2839 this.maskEl.setSize(
2840 Roo.lib.Dom.getViewWidth(true),
2841 Roo.lib.Dom.getViewHeight(true)
2844 if (this.fitwindow) {
2846 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2847 this.height || Roo.lib.Dom.getViewportHeight(true) - 60
2852 if(this.max_width !== 0) {
2854 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2857 this.setSize(w, this.height);
2861 if(this.max_height) {
2862 this.setSize(w,Math.min(
2864 Roo.lib.Dom.getViewportHeight(true) - 60
2870 if(!this.fit_content) {
2871 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2875 this.setSize(w, Math.min(
2877 this.headerEl.getHeight() +
2878 this.footerEl.getHeight() +
2879 this.getChildHeight(this.bodyEl.dom.childNodes),
2880 Roo.lib.Dom.getViewportHeight(true) - 60)
2886 setSize : function(w,h)
2897 if (!this.rendered) {
2901 //this.el.setStyle('display', 'block');
2902 this.el.removeClass('hideing');
2903 this.el.addClass('show');
2905 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
2908 this.el.addClass('in');
2911 this.el.addClass('in');
2914 // not sure how we can show data in here..
2916 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2919 Roo.get(document.body).addClass("x-body-masked");
2921 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2922 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2923 this.maskEl.addClass('show');
2927 this.fireEvent('show', this);
2929 // set zindex here - otherwise it appears to be ignored...
2930 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2933 this.items.forEach( function(e) {
2934 e.layout ? e.layout() : false;
2942 if(this.fireEvent("beforehide", this) !== false){
2943 this.maskEl.removeClass('show');
2944 Roo.get(document.body).removeClass("x-body-masked");
2945 this.el.removeClass('in');
2946 this.el.select('.modal-dialog', true).first().setStyle('transform','');
2948 if(this.animate){ // why
2949 this.el.addClass('hideing');
2951 if (!this.el.hasClass('hideing')) {
2952 return; // it's been shown again...
2954 this.el.removeClass('show');
2955 this.el.removeClass('hideing');
2959 this.el.removeClass('show');
2961 this.fireEvent('hide', this);
2964 isVisible : function()
2967 return this.el.hasClass('show') && !this.el.hasClass('hideing');
2971 addButton : function(str, cb)
2975 var b = Roo.apply({}, { html : str } );
2976 b.xns = b.xns || Roo.bootstrap;
2977 b.xtype = b.xtype || 'Button';
2978 if (typeof(b.listeners) == 'undefined') {
2979 b.listeners = { click : cb.createDelegate(this) };
2982 var btn = Roo.factory(b);
2984 btn.render(this.el.select('.modal-footer div').first());
2990 setDefaultButton : function(btn)
2992 //this.el.select('.modal-footer').()
2996 resizeTo: function(w,h)
3000 this.dialogEl.setWidth(w);
3001 if (this.diff === false) {
3002 this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
3005 this.bodyEl.setHeight(h - this.diff);
3007 this.fireEvent('resize', this);
3010 setContentSize : function(w, h)
3014 onButtonClick: function(btn,e)
3017 this.fireEvent('btnclick', btn.name, e);
3020 * Set the title of the Dialog
3021 * @param {String} str new Title
3023 setTitle: function(str) {
3024 this.titleEl.dom.innerHTML = str;
3027 * Set the body of the Dialog
3028 * @param {String} str new Title
3030 setBody: function(str) {
3031 this.bodyEl.dom.innerHTML = str;
3034 * Set the body of the Dialog using the template
3035 * @param {Obj} data - apply this data to the template and replace the body contents.
3037 applyBody: function(obj)
3040 Roo.log("Error - using apply Body without a template");
3043 this.tmpl.overwrite(this.bodyEl, obj);
3046 getChildHeight : function(child_nodes)
3050 child_nodes.length == 0
3055 var child_height = 0;
3057 for(var i = 0; i < child_nodes.length; i++) {
3060 * for modal with tabs...
3061 if(child_nodes[i].classList.contains('roo-layout-panel')) {
3063 var layout_childs = child_nodes[i].childNodes;
3065 for(var j = 0; j < layout_childs.length; j++) {
3067 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3069 var layout_body_childs = layout_childs[j].childNodes;
3071 for(var k = 0; k < layout_body_childs.length; k++) {
3073 if(layout_body_childs[k].classList.contains('navbar')) {
3074 child_height += layout_body_childs[k].offsetHeight;
3078 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3080 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3082 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3084 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3085 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3100 child_height += child_nodes[i].offsetHeight;
3101 // Roo.log(child_nodes[i].offsetHeight);
3104 return child_height;
3110 Roo.apply(Roo.bootstrap.Modal, {
3112 * Button config that displays a single OK button
3121 * Button config that displays Yes and No buttons
3137 * Button config that displays OK and Cancel buttons
3152 * Button config that displays Yes, No and Cancel buttons
3176 * messagebox - can be used as a replace
3180 * @class Roo.MessageBox
3181 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
3185 Roo.Msg.alert('Status', 'Changes saved successfully.');
3187 // Prompt for user data:
3188 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3190 // process text value...
3194 // Show a dialog using config options:
3196 title:'Save Changes?',
3197 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3198 buttons: Roo.Msg.YESNOCANCEL,
3205 Roo.bootstrap.MessageBox = function(){
3206 var dlg, opt, mask, waitTimer;
3207 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3208 var buttons, activeTextEl, bwidth;
3212 var handleButton = function(button){
3214 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3218 var handleHide = function(){
3220 dlg.el.removeClass(opt.cls);
3223 // Roo.TaskMgr.stop(waitTimer);
3224 // waitTimer = null;
3229 var updateButtons = function(b){
3232 buttons["ok"].hide();
3233 buttons["cancel"].hide();
3234 buttons["yes"].hide();
3235 buttons["no"].hide();
3236 //dlg.footer.dom.style.display = 'none';
3239 dlg.footerEl.dom.style.display = '';
3240 for(var k in buttons){
3241 if(typeof buttons[k] != "function"){
3244 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3245 width += buttons[k].el.getWidth()+15;
3255 var handleEsc = function(d, k, e){
3256 if(opt && opt.closable !== false){
3266 * Returns a reference to the underlying {@link Roo.BasicDialog} element
3267 * @return {Roo.BasicDialog} The BasicDialog element
3269 getDialog : function(){
3271 dlg = new Roo.bootstrap.Modal( {
3274 //constraintoviewport:false,
3276 //collapsible : false,
3281 //buttonAlign:"center",
3282 closeClick : function(){
3283 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3286 handleButton("cancel");
3291 dlg.on("hide", handleHide);
3293 //dlg.addKeyListener(27, handleEsc);
3295 this.buttons = buttons;
3296 var bt = this.buttonText;
3297 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3298 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3299 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3300 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3302 bodyEl = dlg.bodyEl.createChild({
3304 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3305 '<textarea class="roo-mb-textarea"></textarea>' +
3306 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
3308 msgEl = bodyEl.dom.firstChild;
3309 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3310 textboxEl.enableDisplayMode();
3311 textboxEl.addKeyListener([10,13], function(){
3312 if(dlg.isVisible() && opt && opt.buttons){
3315 }else if(opt.buttons.yes){
3316 handleButton("yes");
3320 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3321 textareaEl.enableDisplayMode();
3322 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3323 progressEl.enableDisplayMode();
3325 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3326 var pf = progressEl.dom.firstChild;
3328 pp = Roo.get(pf.firstChild);
3329 pp.setHeight(pf.offsetHeight);
3337 * Updates the message box body text
3338 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3339 * the XHTML-compliant non-breaking space character '&#160;')
3340 * @return {Roo.MessageBox} This message box
3342 updateText : function(text)
3344 if(!dlg.isVisible() && !opt.width){
3345 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3346 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3348 msgEl.innerHTML = text || ' ';
3350 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3351 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3353 Math.min(opt.width || cw , this.maxWidth),
3354 Math.max(opt.minWidth || this.minWidth, bwidth)
3357 activeTextEl.setWidth(w);
3359 if(dlg.isVisible()){
3360 dlg.fixedcenter = false;
3362 // to big, make it scroll. = But as usual stupid IE does not support
3365 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3366 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3367 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3369 bodyEl.dom.style.height = '';
3370 bodyEl.dom.style.overflowY = '';
3373 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3375 bodyEl.dom.style.overflowX = '';
3378 dlg.setContentSize(w, bodyEl.getHeight());
3379 if(dlg.isVisible()){
3380 dlg.fixedcenter = true;
3386 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
3387 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3388 * @param {Number} value Any number between 0 and 1 (e.g., .5)
3389 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3390 * @return {Roo.MessageBox} This message box
3392 updateProgress : function(value, text){
3394 this.updateText(text);
3397 if (pp) { // weird bug on my firefox - for some reason this is not defined
3398 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3399 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3405 * Returns true if the message box is currently displayed
3406 * @return {Boolean} True if the message box is visible, else false
3408 isVisible : function(){
3409 return dlg && dlg.isVisible();
3413 * Hides the message box if it is displayed
3416 if(this.isVisible()){
3422 * Displays a new message box, or reinitializes an existing message box, based on the config options
3423 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3424 * The following config object properties are supported:
3426 Property Type Description
3427 ---------- --------------- ------------------------------------------------------------------------------------
3428 animEl String/Element An id or Element from which the message box should animate as it opens and
3429 closes (defaults to undefined)
3430 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3431 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3432 closable Boolean False to hide the top-right close button (defaults to true). Note that
3433 progress and wait dialogs will ignore this property and always hide the
3434 close button as they can only be closed programmatically.
3435 cls String A custom CSS class to apply to the message box element
3436 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3437 displayed (defaults to 75)
3438 fn Function A callback function to execute after closing the dialog. The arguments to the
3439 function will be btn (the name of the button that was clicked, if applicable,
3440 e.g. "ok"), and text (the value of the active text field, if applicable).
3441 Progress and wait dialogs will ignore this option since they do not respond to
3442 user actions and can only be closed programmatically, so any required function
3443 should be called by the same code after it closes the dialog.
3444 icon String A CSS class that provides a background image to be used as an icon for
3445 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3446 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3447 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3448 modal Boolean False to allow user interaction with the page while the message box is
3449 displayed (defaults to true)
3450 msg String A string that will replace the existing message box body text (defaults
3451 to the XHTML-compliant non-breaking space character ' ')
3452 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3453 progress Boolean True to display a progress bar (defaults to false)
3454 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3455 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3456 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3457 title String The title text
3458 value String The string value to set into the active textbox element if displayed
3459 wait Boolean True to display a progress bar (defaults to false)
3460 width Number The width of the dialog in pixels
3467 msg: 'Please enter your address:',
3469 buttons: Roo.MessageBox.OKCANCEL,
3472 animEl: 'addAddressBtn'
3475 * @param {Object} config Configuration options
3476 * @return {Roo.MessageBox} This message box
3478 show : function(options)
3481 // this causes nightmares if you show one dialog after another
3482 // especially on callbacks..
3484 if(this.isVisible()){
3487 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3488 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3489 Roo.log("New Dialog Message:" + options.msg )
3490 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3491 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3494 var d = this.getDialog();
3496 d.setTitle(opt.title || " ");
3497 d.closeEl.setDisplayed(opt.closable !== false);
3498 activeTextEl = textboxEl;
3499 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3504 textareaEl.setHeight(typeof opt.multiline == "number" ?
3505 opt.multiline : this.defaultTextHeight);
3506 activeTextEl = textareaEl;
3515 progressEl.setDisplayed(opt.progress === true);
3516 this.updateProgress(0);
3517 activeTextEl.dom.value = opt.value || "";
3519 dlg.setDefaultButton(activeTextEl);
3521 var bs = opt.buttons;
3525 }else if(bs && bs.yes){
3526 db = buttons["yes"];
3528 dlg.setDefaultButton(db);
3530 bwidth = updateButtons(opt.buttons);
3531 this.updateText(opt.msg);
3533 d.el.addClass(opt.cls);
3535 d.proxyDrag = opt.proxyDrag === true;
3536 d.modal = opt.modal !== false;
3537 d.mask = opt.modal !== false ? mask : false;
3539 // force it to the end of the z-index stack so it gets a cursor in FF
3540 document.body.appendChild(dlg.el.dom);
3541 d.animateTarget = null;
3542 d.show(options.animEl);
3548 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3549 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3550 * and closing the message box when the process is complete.
3551 * @param {String} title The title bar text
3552 * @param {String} msg The message box body text
3553 * @return {Roo.MessageBox} This message box
3555 progress : function(title, msg){
3562 minWidth: this.minProgressWidth,
3569 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3570 * If a callback function is passed it will be called after the user clicks the button, and the
3571 * id of the button that was clicked will be passed as the only parameter to the callback
3572 * (could also be the top-right close button).
3573 * @param {String} title The title bar text
3574 * @param {String} msg The message box body text
3575 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3576 * @param {Object} scope (optional) The scope of the callback function
3577 * @return {Roo.MessageBox} This message box
3579 alert : function(title, msg, fn, scope)
3594 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3595 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3596 * You are responsible for closing the message box when the process is complete.
3597 * @param {String} msg The message box body text
3598 * @param {String} title (optional) The title bar text
3599 * @return {Roo.MessageBox} This message box
3601 wait : function(msg, title){
3612 waitTimer = Roo.TaskMgr.start({
3614 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3622 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3623 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3624 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3625 * @param {String} title The title bar text
3626 * @param {String} msg The message box body text
3627 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3628 * @param {Object} scope (optional) The scope of the callback function
3629 * @return {Roo.MessageBox} This message box
3631 confirm : function(title, msg, fn, scope){
3635 buttons: this.YESNO,
3644 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3645 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3646 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3647 * (could also be the top-right close button) and the text that was entered will be passed as the two
3648 * parameters to the callback.
3649 * @param {String} title The title bar text
3650 * @param {String} msg The message box body text
3651 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3652 * @param {Object} scope (optional) The scope of the callback function
3653 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3654 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3655 * @return {Roo.MessageBox} This message box
3657 prompt : function(title, msg, fn, scope, multiline){
3661 buttons: this.OKCANCEL,
3666 multiline: multiline,
3673 * Button config that displays a single OK button
3678 * Button config that displays Yes and No buttons
3681 YESNO : {yes:true, no:true},
3683 * Button config that displays OK and Cancel buttons
3686 OKCANCEL : {ok:true, cancel:true},
3688 * Button config that displays Yes, No and Cancel buttons
3691 YESNOCANCEL : {yes:true, no:true, cancel:true},
3694 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3697 defaultTextHeight : 75,
3699 * The maximum width in pixels of the message box (defaults to 600)
3704 * The minimum width in pixels of the message box (defaults to 100)
3709 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3710 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3713 minProgressWidth : 250,
3715 * An object containing the default button text strings that can be overriden for localized language support.
3716 * Supported properties are: ok, cancel, yes and no.
3717 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3730 * Shorthand for {@link Roo.MessageBox}
3732 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3733 Roo.Msg = Roo.Msg || Roo.MessageBox;
3742 * @class Roo.bootstrap.Navbar
3743 * @extends Roo.bootstrap.Component
3744 * Bootstrap Navbar class
3747 * Create a new Navbar
3748 * @param {Object} config The config object
3752 Roo.bootstrap.Navbar = function(config){
3753 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3757 * @event beforetoggle
3758 * Fire before toggle the menu
3759 * @param {Roo.EventObject} e
3761 "beforetoggle" : true
3765 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3774 getAutoCreate : function(){
3777 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3781 initEvents :function ()
3783 //Roo.log(this.el.select('.navbar-toggle',true));
3784 this.el.select('.navbar-toggle',true).on('click', function() {
3785 if(this.fireEvent('beforetoggle', this) !== false){
3786 this.el.select('.navbar-collapse',true).toggleClass('in');
3796 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3798 var size = this.el.getSize();
3799 this.maskEl.setSize(size.width, size.height);
3800 this.maskEl.enableDisplayMode("block");
3809 getChildContainer : function()
3811 if (this.el.select('.collapse').getCount()) {
3812 return this.el.select('.collapse',true).first();
3845 * @class Roo.bootstrap.NavSimplebar
3846 * @extends Roo.bootstrap.Navbar
3847 * Bootstrap Sidebar class
3849 * @cfg {Boolean} inverse is inverted color
3851 * @cfg {String} type (nav | pills | tabs)
3852 * @cfg {Boolean} arrangement stacked | justified
3853 * @cfg {String} align (left | right) alignment
3855 * @cfg {Boolean} main (true|false) main nav bar? default false
3856 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3858 * @cfg {String} tag (header|footer|nav|div) default is nav
3864 * Create a new Sidebar
3865 * @param {Object} config The config object
3869 Roo.bootstrap.NavSimplebar = function(config){
3870 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3873 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3889 getAutoCreate : function(){
3893 tag : this.tag || 'div',
3906 this.type = this.type || 'nav';
3907 if (['tabs','pills'].indexOf(this.type)!==-1) {
3908 cfg.cn[0].cls += ' nav-' + this.type
3912 if (this.type!=='nav') {
3913 Roo.log('nav type must be nav/tabs/pills')
3915 cfg.cn[0].cls += ' navbar-nav'
3921 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3922 cfg.cn[0].cls += ' nav-' + this.arrangement;
3926 if (this.align === 'right') {
3927 cfg.cn[0].cls += ' navbar-right';
3931 cfg.cls += ' navbar-inverse';
3955 * navbar-expand-md fixed-top
3959 * @class Roo.bootstrap.NavHeaderbar
3960 * @extends Roo.bootstrap.NavSimplebar
3961 * Bootstrap Sidebar class
3963 * @cfg {String} brand what is brand
3964 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3965 * @cfg {String} brand_href href of the brand
3966 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
3967 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3968 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3969 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3972 * Create a new Sidebar
3973 * @param {Object} config The config object
3977 Roo.bootstrap.NavHeaderbar = function(config){
3978 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3982 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3989 desktopCenter : false,
3992 getAutoCreate : function(){
3995 tag: this.nav || 'nav',
3996 cls: 'navbar navbar-expand-md',
4002 if (this.desktopCenter) {
4003 cn.push({cls : 'container', cn : []});
4010 cls: 'navbar-header',
4015 cls: 'navbar-toggle navbar-toggler',
4016 'data-toggle': 'collapse',
4021 html: 'Toggle navigation'
4025 cls: 'icon-bar navbar-toggler-icon'
4043 cls: 'collapse navbar-collapse',
4047 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4049 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4050 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4052 // tag can override this..
4054 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
4057 if (this.brand !== '') {
4060 href: this.brand_href ? this.brand_href : '#',
4061 cls: 'navbar-brand',
4069 cfg.cls += ' main-nav';
4077 getHeaderChildContainer : function()
4079 if (this.srButton && this.el.select('.navbar-header').getCount()) {
4080 return this.el.select('.navbar-header',true).first();
4083 return this.getChildContainer();
4087 initEvents : function()
4089 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4091 if (this.autohide) {
4096 Roo.get(document).on('scroll',function(e) {
4097 var ns = Roo.get(document).getScroll().top;
4098 var os = prevScroll;
4102 ft.removeClass('slideDown');
4103 ft.addClass('slideUp');
4106 ft.removeClass('slideUp');
4107 ft.addClass('slideDown');
4128 * @class Roo.bootstrap.NavSidebar
4129 * @extends Roo.bootstrap.Navbar
4130 * Bootstrap Sidebar class
4133 * Create a new Sidebar
4134 * @param {Object} config The config object
4138 Roo.bootstrap.NavSidebar = function(config){
4139 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4142 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
4144 sidebar : true, // used by Navbar Item and NavbarGroup at present...
4146 getAutoCreate : function(){
4151 cls: 'sidebar sidebar-nav'
4173 * @class Roo.bootstrap.NavGroup
4174 * @extends Roo.bootstrap.Component
4175 * Bootstrap NavGroup class
4176 * @cfg {String} align (left|right)
4177 * @cfg {Boolean} inverse
4178 * @cfg {String} type (nav|pills|tab) default nav
4179 * @cfg {String} navId - reference Id for navbar.
4183 * Create a new nav group
4184 * @param {Object} config The config object
4187 Roo.bootstrap.NavGroup = function(config){
4188 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4191 Roo.bootstrap.NavGroup.register(this);
4195 * Fires when the active item changes
4196 * @param {Roo.bootstrap.NavGroup} this
4197 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4198 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
4205 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
4216 getAutoCreate : function()
4218 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4225 if (['tabs','pills'].indexOf(this.type)!==-1) {
4226 cfg.cls += ' nav-' + this.type
4228 if (this.type!=='nav') {
4229 Roo.log('nav type must be nav/tabs/pills')
4231 cfg.cls += ' navbar-nav mr-auto'
4234 if (this.parent() && this.parent().sidebar) {
4237 cls: 'dashboard-menu sidebar-menu'
4243 if (this.form === true) {
4249 if (this.align === 'right') {
4250 cfg.cls += ' navbar-right';
4252 cfg.cls += ' navbar-left';
4256 if (this.align === 'right') {
4257 cfg.cls += ' navbar-right';
4261 cfg.cls += ' navbar-inverse';
4269 * sets the active Navigation item
4270 * @param {Roo.bootstrap.NavItem} the new current navitem
4272 setActiveItem : function(item)
4275 Roo.each(this.navItems, function(v){
4280 v.setActive(false, true);
4287 item.setActive(true, true);
4288 this.fireEvent('changed', this, item, prev);
4293 * gets the active Navigation item
4294 * @return {Roo.bootstrap.NavItem} the current navitem
4296 getActive : function()
4300 Roo.each(this.navItems, function(v){
4311 indexOfNav : function()
4315 Roo.each(this.navItems, function(v,i){
4326 * adds a Navigation item
4327 * @param {Roo.bootstrap.NavItem} the navitem to add
4329 addItem : function(cfg)
4331 var cn = new Roo.bootstrap.NavItem(cfg);
4333 cn.parentId = this.id;
4334 cn.onRender(this.el, null);
4338 * register a Navigation item
4339 * @param {Roo.bootstrap.NavItem} the navitem to add
4341 register : function(item)
4343 this.navItems.push( item);
4344 item.navId = this.navId;
4349 * clear all the Navigation item
4352 clearAll : function()
4355 this.el.dom.innerHTML = '';
4358 getNavItem: function(tabId)
4361 Roo.each(this.navItems, function(e) {
4362 if (e.tabId == tabId) {
4372 setActiveNext : function()
4374 var i = this.indexOfNav(this.getActive());
4375 if (i > this.navItems.length) {
4378 this.setActiveItem(this.navItems[i+1]);
4380 setActivePrev : function()
4382 var i = this.indexOfNav(this.getActive());
4386 this.setActiveItem(this.navItems[i-1]);
4388 clearWasActive : function(except) {
4389 Roo.each(this.navItems, function(e) {
4390 if (e.tabId != except.tabId && e.was_active) {
4391 e.was_active = false;
4398 getWasActive : function ()
4401 Roo.each(this.navItems, function(e) {
4416 Roo.apply(Roo.bootstrap.NavGroup, {
4420 * register a Navigation Group
4421 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4423 register : function(navgrp)
4425 this.groups[navgrp.navId] = navgrp;
4429 * fetch a Navigation Group based on the navigation ID
4430 * @param {string} the navgroup to add
4431 * @returns {Roo.bootstrap.NavGroup} the navgroup
4433 get: function(navId) {
4434 if (typeof(this.groups[navId]) == 'undefined') {
4436 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4438 return this.groups[navId] ;
4453 * @class Roo.bootstrap.NavItem
4454 * @extends Roo.bootstrap.Component
4455 * Bootstrap Navbar.NavItem class
4456 * @cfg {String} href link to
4457 * @cfg {String} html content of button
4458 * @cfg {String} badge text inside badge
4459 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4460 * @cfg {String} glyphicon name of glyphicon
4461 * @cfg {String} icon name of font awesome icon
4462 * @cfg {Boolean} active Is item active
4463 * @cfg {Boolean} disabled Is item disabled
4465 * @cfg {Boolean} preventDefault (true | false) default false
4466 * @cfg {String} tabId the tab that this item activates.
4467 * @cfg {String} tagtype (a|span) render as a href or span?
4468 * @cfg {Boolean} animateRef (true|false) link to element default false
4471 * Create a new Navbar Item
4472 * @param {Object} config The config object
4474 Roo.bootstrap.NavItem = function(config){
4475 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4480 * The raw click event for the entire grid.
4481 * @param {Roo.EventObject} e
4486 * Fires when the active item active state changes
4487 * @param {Roo.bootstrap.NavItem} this
4488 * @param {boolean} state the new state
4494 * Fires when scroll to element
4495 * @param {Roo.bootstrap.NavItem} this
4496 * @param {Object} options
4497 * @param {Roo.EventObject} e
4505 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4513 preventDefault : false,
4520 getAutoCreate : function(){
4529 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4531 if (this.disabled) {
4532 cfg.cls += ' disabled';
4535 if (this.href || this.html || this.glyphicon || this.icon) {
4539 href : this.href || "#",
4540 html: this.html || ''
4543 if (this.tagtype == 'a') {
4544 cfg.cn[0].cls = 'nav-link';
4547 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4550 if(this.glyphicon) {
4551 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4556 cfg.cn[0].html += " <span class='caret'></span>";
4560 if (this.badge !== '') {
4562 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4570 initEvents: function()
4572 if (typeof (this.menu) != 'undefined') {
4573 this.menu.parentType = this.xtype;
4574 this.menu.triggerEl = this.el;
4575 this.menu = this.addxtype(Roo.apply({}, this.menu));
4578 this.el.select('a',true).on('click', this.onClick, this);
4580 if(this.tagtype == 'span'){
4581 this.el.select('span',true).on('click', this.onClick, this);
4584 // at this point parent should be available..
4585 this.parent().register(this);
4588 onClick : function(e)
4590 if (e.getTarget('.dropdown-menu-item')) {
4591 // did you click on a menu itemm.... - then don't trigger onclick..
4596 this.preventDefault ||
4599 Roo.log("NavItem - prevent Default?");
4603 if (this.disabled) {
4607 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4608 if (tg && tg.transition) {
4609 Roo.log("waiting for the transitionend");
4615 //Roo.log("fire event clicked");
4616 if(this.fireEvent('click', this, e) === false){
4620 if(this.tagtype == 'span'){
4624 //Roo.log(this.href);
4625 var ael = this.el.select('a',true).first();
4628 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4629 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4630 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4631 return; // ignore... - it's a 'hash' to another page.
4633 Roo.log("NavItem - prevent Default?");
4635 this.scrollToElement(e);
4639 var p = this.parent();
4641 if (['tabs','pills'].indexOf(p.type)!==-1) {
4642 if (typeof(p.setActiveItem) !== 'undefined') {
4643 p.setActiveItem(this);
4647 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4648 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4649 // remove the collapsed menu expand...
4650 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4654 isActive: function () {
4657 setActive : function(state, fire, is_was_active)
4659 if (this.active && !state && this.navId) {
4660 this.was_active = true;
4661 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4663 nv.clearWasActive(this);
4667 this.active = state;
4670 this.el.removeClass('active');
4671 } else if (!this.el.hasClass('active')) {
4672 this.el.addClass('active');
4675 this.fireEvent('changed', this, state);
4678 // show a panel if it's registered and related..
4680 if (!this.navId || !this.tabId || !state || is_was_active) {
4684 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4688 var pan = tg.getPanelByName(this.tabId);
4692 // if we can not flip to new panel - go back to old nav highlight..
4693 if (false == tg.showPanel(pan)) {
4694 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4696 var onav = nv.getWasActive();
4698 onav.setActive(true, false, true);
4707 // this should not be here...
4708 setDisabled : function(state)
4710 this.disabled = state;
4712 this.el.removeClass('disabled');
4713 } else if (!this.el.hasClass('disabled')) {
4714 this.el.addClass('disabled');
4720 * Fetch the element to display the tooltip on.
4721 * @return {Roo.Element} defaults to this.el
4723 tooltipEl : function()
4725 return this.el.select('' + this.tagtype + '', true).first();
4728 scrollToElement : function(e)
4730 var c = document.body;
4733 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4735 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4736 c = document.documentElement;
4739 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4745 var o = target.calcOffsetsTo(c);
4752 this.fireEvent('scrollto', this, options, e);
4754 Roo.get(c).scrollTo('top', options.value, true);
4767 * <span> icon </span>
4768 * <span> text </span>
4769 * <span>badge </span>
4773 * @class Roo.bootstrap.NavSidebarItem
4774 * @extends Roo.bootstrap.NavItem
4775 * Bootstrap Navbar.NavSidebarItem class
4776 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4777 * {Boolean} open is the menu open
4778 * {Boolean} buttonView use button as the tigger el rather that a (default false)
4779 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4780 * {String} buttonSize (sm|md|lg)the extra classes for the button
4781 * {Boolean} showArrow show arrow next to the text (default true)
4783 * Create a new Navbar Button
4784 * @param {Object} config The config object
4786 Roo.bootstrap.NavSidebarItem = function(config){
4787 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4792 * The raw click event for the entire grid.
4793 * @param {Roo.EventObject} e
4798 * Fires when the active item active state changes
4799 * @param {Roo.bootstrap.NavSidebarItem} this
4800 * @param {boolean} state the new state
4808 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4810 badgeWeight : 'default',
4816 buttonWeight : 'default',
4822 getAutoCreate : function(){
4827 href : this.href || '#',
4833 if(this.buttonView){
4836 href : this.href || '#',
4837 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4850 cfg.cls += ' active';
4853 if (this.disabled) {
4854 cfg.cls += ' disabled';
4857 cfg.cls += ' open x-open';
4860 if (this.glyphicon || this.icon) {
4861 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4862 a.cn.push({ tag : 'i', cls : c }) ;
4865 if(!this.buttonView){
4868 html : this.html || ''
4875 if (this.badge !== '') {
4876 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4882 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4885 a.cls += ' dropdown-toggle treeview' ;
4891 initEvents : function()
4893 if (typeof (this.menu) != 'undefined') {
4894 this.menu.parentType = this.xtype;
4895 this.menu.triggerEl = this.el;
4896 this.menu = this.addxtype(Roo.apply({}, this.menu));
4899 this.el.on('click', this.onClick, this);
4901 if(this.badge !== ''){
4902 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4907 onClick : function(e)
4914 if(this.preventDefault){
4918 this.fireEvent('click', this);
4921 disable : function()
4923 this.setDisabled(true);
4928 this.setDisabled(false);
4931 setDisabled : function(state)
4933 if(this.disabled == state){
4937 this.disabled = state;
4940 this.el.addClass('disabled');
4944 this.el.removeClass('disabled');
4949 setActive : function(state)
4951 if(this.active == state){
4955 this.active = state;
4958 this.el.addClass('active');
4962 this.el.removeClass('active');
4967 isActive: function ()
4972 setBadge : function(str)
4978 this.badgeEl.dom.innerHTML = str;
4995 * @class Roo.bootstrap.Row
4996 * @extends Roo.bootstrap.Component
4997 * Bootstrap Row class (contains columns...)
5001 * @param {Object} config The config object
5004 Roo.bootstrap.Row = function(config){
5005 Roo.bootstrap.Row.superclass.constructor.call(this, config);
5008 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
5010 getAutoCreate : function(){
5029 * @class Roo.bootstrap.Element
5030 * @extends Roo.bootstrap.Component
5031 * Bootstrap Element class
5032 * @cfg {String} html contents of the element
5033 * @cfg {String} tag tag of the element
5034 * @cfg {String} cls class of the element
5035 * @cfg {Boolean} preventDefault (true|false) default false
5036 * @cfg {Boolean} clickable (true|false) default false
5039 * Create a new Element
5040 * @param {Object} config The config object
5043 Roo.bootstrap.Element = function(config){
5044 Roo.bootstrap.Element.superclass.constructor.call(this, config);
5050 * When a element is chick
5051 * @param {Roo.bootstrap.Element} this
5052 * @param {Roo.EventObject} e
5058 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
5063 preventDefault: false,
5066 getAutoCreate : function(){
5070 // cls: this.cls, double assign in parent class Component.js :: onRender
5077 initEvents: function()
5079 Roo.bootstrap.Element.superclass.initEvents.call(this);
5082 this.el.on('click', this.onClick, this);
5087 onClick : function(e)
5089 if(this.preventDefault){
5093 this.fireEvent('click', this, e);
5096 getValue : function()
5098 return this.el.dom.innerHTML;
5101 setValue : function(value)
5103 this.el.dom.innerHTML = value;
5118 * @class Roo.bootstrap.Pagination
5119 * @extends Roo.bootstrap.Component
5120 * Bootstrap Pagination class
5121 * @cfg {String} size xs | sm | md | lg
5122 * @cfg {Boolean} inverse false | true
5125 * Create a new Pagination
5126 * @param {Object} config The config object
5129 Roo.bootstrap.Pagination = function(config){
5130 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5133 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
5139 getAutoCreate : function(){
5145 cfg.cls += ' inverse';
5151 cfg.cls += " " + this.cls;
5169 * @class Roo.bootstrap.PaginationItem
5170 * @extends Roo.bootstrap.Component
5171 * Bootstrap PaginationItem class
5172 * @cfg {String} html text
5173 * @cfg {String} href the link
5174 * @cfg {Boolean} preventDefault (true | false) default true
5175 * @cfg {Boolean} active (true | false) default false
5176 * @cfg {Boolean} disabled default false
5180 * Create a new PaginationItem
5181 * @param {Object} config The config object
5185 Roo.bootstrap.PaginationItem = function(config){
5186 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5191 * The raw click event for the entire grid.
5192 * @param {Roo.EventObject} e
5198 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
5202 preventDefault: true,
5207 getAutoCreate : function(){
5213 href : this.href ? this.href : '#',
5214 html : this.html ? this.html : ''
5224 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5228 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5234 initEvents: function() {
5236 this.el.on('click', this.onClick, this);
5239 onClick : function(e)
5241 Roo.log('PaginationItem on click ');
5242 if(this.preventDefault){
5250 this.fireEvent('click', this, e);
5266 * @class Roo.bootstrap.Slider
5267 * @extends Roo.bootstrap.Component
5268 * Bootstrap Slider class
5271 * Create a new Slider
5272 * @param {Object} config The config object
5275 Roo.bootstrap.Slider = function(config){
5276 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5279 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
5281 getAutoCreate : function(){
5285 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5289 cls: 'ui-slider-handle ui-state-default ui-corner-all'
5301 * Ext JS Library 1.1.1
5302 * Copyright(c) 2006-2007, Ext JS, LLC.
5304 * Originally Released Under LGPL - original licence link has changed is not relivant.
5307 * <script type="text/javascript">
5312 * @class Roo.grid.ColumnModel
5313 * @extends Roo.util.Observable
5314 * This is the default implementation of a ColumnModel used by the Grid. It defines
5315 * the columns in the grid.
5318 var colModel = new Roo.grid.ColumnModel([
5319 {header: "Ticker", width: 60, sortable: true, locked: true},
5320 {header: "Company Name", width: 150, sortable: true},
5321 {header: "Market Cap.", width: 100, sortable: true},
5322 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5323 {header: "Employees", width: 100, sortable: true, resizable: false}
5328 * The config options listed for this class are options which may appear in each
5329 * individual column definition.
5330 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5332 * @param {Object} config An Array of column config objects. See this class's
5333 * config objects for details.
5335 Roo.grid.ColumnModel = function(config){
5337 * The config passed into the constructor
5339 this.config = config;
5342 // if no id, create one
5343 // if the column does not have a dataIndex mapping,
5344 // map it to the order it is in the config
5345 for(var i = 0, len = config.length; i < len; i++){
5347 if(typeof c.dataIndex == "undefined"){
5350 if(typeof c.renderer == "string"){
5351 c.renderer = Roo.util.Format[c.renderer];
5353 if(typeof c.id == "undefined"){
5356 if(c.editor && c.editor.xtype){
5357 c.editor = Roo.factory(c.editor, Roo.grid);
5359 if(c.editor && c.editor.isFormField){
5360 c.editor = new Roo.grid.GridEditor(c.editor);
5362 this.lookup[c.id] = c;
5366 * The width of columns which have no width specified (defaults to 100)
5369 this.defaultWidth = 100;
5372 * Default sortable of columns which have no sortable specified (defaults to false)
5375 this.defaultSortable = false;
5379 * @event widthchange
5380 * Fires when the width of a column changes.
5381 * @param {ColumnModel} this
5382 * @param {Number} columnIndex The column index
5383 * @param {Number} newWidth The new width
5385 "widthchange": true,
5387 * @event headerchange
5388 * Fires when the text of a header changes.
5389 * @param {ColumnModel} this
5390 * @param {Number} columnIndex The column index
5391 * @param {Number} newText The new header text
5393 "headerchange": true,
5395 * @event hiddenchange
5396 * Fires when a column is hidden or "unhidden".
5397 * @param {ColumnModel} this
5398 * @param {Number} columnIndex The column index
5399 * @param {Boolean} hidden true if hidden, false otherwise
5401 "hiddenchange": true,
5403 * @event columnmoved
5404 * Fires when a column is moved.
5405 * @param {ColumnModel} this
5406 * @param {Number} oldIndex
5407 * @param {Number} newIndex
5409 "columnmoved" : true,
5411 * @event columlockchange
5412 * Fires when a column's locked state is changed
5413 * @param {ColumnModel} this
5414 * @param {Number} colIndex
5415 * @param {Boolean} locked true if locked
5417 "columnlockchange" : true
5419 Roo.grid.ColumnModel.superclass.constructor.call(this);
5421 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5423 * @cfg {String} header The header text to display in the Grid view.
5426 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5427 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5428 * specified, the column's index is used as an index into the Record's data Array.
5431 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5432 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5435 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5436 * Defaults to the value of the {@link #defaultSortable} property.
5437 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5440 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5443 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5446 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5449 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5452 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5453 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5454 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5455 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5458 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5461 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5464 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
5467 * @cfg {String} cursor (Optional)
5470 * @cfg {String} tooltip (Optional)
5473 * @cfg {Number} xs (Optional)
5476 * @cfg {Number} sm (Optional)
5479 * @cfg {Number} md (Optional)
5482 * @cfg {Number} lg (Optional)
5485 * Returns the id of the column at the specified index.
5486 * @param {Number} index The column index
5487 * @return {String} the id
5489 getColumnId : function(index){
5490 return this.config[index].id;
5494 * Returns the column for a specified id.
5495 * @param {String} id The column id
5496 * @return {Object} the column
5498 getColumnById : function(id){
5499 return this.lookup[id];
5504 * Returns the column for a specified dataIndex.
5505 * @param {String} dataIndex The column dataIndex
5506 * @return {Object|Boolean} the column or false if not found
5508 getColumnByDataIndex: function(dataIndex){
5509 var index = this.findColumnIndex(dataIndex);
5510 return index > -1 ? this.config[index] : false;
5514 * Returns the index for a specified column id.
5515 * @param {String} id The column id
5516 * @return {Number} the index, or -1 if not found
5518 getIndexById : function(id){
5519 for(var i = 0, len = this.config.length; i < len; i++){
5520 if(this.config[i].id == id){
5528 * Returns the index for a specified column dataIndex.
5529 * @param {String} dataIndex The column dataIndex
5530 * @return {Number} the index, or -1 if not found
5533 findColumnIndex : function(dataIndex){
5534 for(var i = 0, len = this.config.length; i < len; i++){
5535 if(this.config[i].dataIndex == dataIndex){
5543 moveColumn : function(oldIndex, newIndex){
5544 var c = this.config[oldIndex];
5545 this.config.splice(oldIndex, 1);
5546 this.config.splice(newIndex, 0, c);
5547 this.dataMap = null;
5548 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5551 isLocked : function(colIndex){
5552 return this.config[colIndex].locked === true;
5555 setLocked : function(colIndex, value, suppressEvent){
5556 if(this.isLocked(colIndex) == value){
5559 this.config[colIndex].locked = value;
5561 this.fireEvent("columnlockchange", this, colIndex, value);
5565 getTotalLockedWidth : function(){
5567 for(var i = 0; i < this.config.length; i++){
5568 if(this.isLocked(i) && !this.isHidden(i)){
5569 this.totalWidth += this.getColumnWidth(i);
5575 getLockedCount : function(){
5576 for(var i = 0, len = this.config.length; i < len; i++){
5577 if(!this.isLocked(i)){
5582 return this.config.length;
5586 * Returns the number of columns.
5589 getColumnCount : function(visibleOnly){
5590 if(visibleOnly === true){
5592 for(var i = 0, len = this.config.length; i < len; i++){
5593 if(!this.isHidden(i)){
5599 return this.config.length;
5603 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5604 * @param {Function} fn
5605 * @param {Object} scope (optional)
5606 * @return {Array} result
5608 getColumnsBy : function(fn, scope){
5610 for(var i = 0, len = this.config.length; i < len; i++){
5611 var c = this.config[i];
5612 if(fn.call(scope||this, c, i) === true){
5620 * Returns true if the specified column is sortable.
5621 * @param {Number} col The column index
5624 isSortable : function(col){
5625 if(typeof this.config[col].sortable == "undefined"){
5626 return this.defaultSortable;
5628 return this.config[col].sortable;
5632 * Returns the rendering (formatting) function defined for the column.
5633 * @param {Number} col The column index.
5634 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5636 getRenderer : function(col){
5637 if(!this.config[col].renderer){
5638 return Roo.grid.ColumnModel.defaultRenderer;
5640 return this.config[col].renderer;
5644 * Sets the rendering (formatting) function for a column.
5645 * @param {Number} col The column index
5646 * @param {Function} fn The function to use to process the cell's raw data
5647 * to return HTML markup for the grid view. The render function is called with
5648 * the following parameters:<ul>
5649 * <li>Data value.</li>
5650 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5651 * <li>css A CSS style string to apply to the table cell.</li>
5652 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5653 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5654 * <li>Row index</li>
5655 * <li>Column index</li>
5656 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5658 setRenderer : function(col, fn){
5659 this.config[col].renderer = fn;
5663 * Returns the width for the specified column.
5664 * @param {Number} col The column index
5667 getColumnWidth : function(col){
5668 return this.config[col].width * 1 || this.defaultWidth;
5672 * Sets the width for a column.
5673 * @param {Number} col The column index
5674 * @param {Number} width The new width
5676 setColumnWidth : function(col, width, suppressEvent){
5677 this.config[col].width = width;
5678 this.totalWidth = null;
5680 this.fireEvent("widthchange", this, col, width);
5685 * Returns the total width of all columns.
5686 * @param {Boolean} includeHidden True to include hidden column widths
5689 getTotalWidth : function(includeHidden){
5690 if(!this.totalWidth){
5691 this.totalWidth = 0;
5692 for(var i = 0, len = this.config.length; i < len; i++){
5693 if(includeHidden || !this.isHidden(i)){
5694 this.totalWidth += this.getColumnWidth(i);
5698 return this.totalWidth;
5702 * Returns the header for the specified column.
5703 * @param {Number} col The column index
5706 getColumnHeader : function(col){
5707 return this.config[col].header;
5711 * Sets the header for a column.
5712 * @param {Number} col The column index
5713 * @param {String} header The new header
5715 setColumnHeader : function(col, header){
5716 this.config[col].header = header;
5717 this.fireEvent("headerchange", this, col, header);
5721 * Returns the tooltip for the specified column.
5722 * @param {Number} col The column index
5725 getColumnTooltip : function(col){
5726 return this.config[col].tooltip;
5729 * Sets the tooltip for a column.
5730 * @param {Number} col The column index
5731 * @param {String} tooltip The new tooltip
5733 setColumnTooltip : function(col, tooltip){
5734 this.config[col].tooltip = tooltip;
5738 * Returns the dataIndex for the specified column.
5739 * @param {Number} col The column index
5742 getDataIndex : function(col){
5743 return this.config[col].dataIndex;
5747 * Sets the dataIndex for a column.
5748 * @param {Number} col The column index
5749 * @param {Number} dataIndex The new dataIndex
5751 setDataIndex : function(col, dataIndex){
5752 this.config[col].dataIndex = dataIndex;
5758 * Returns true if the cell is editable.
5759 * @param {Number} colIndex The column index
5760 * @param {Number} rowIndex The row index - this is nto actually used..?
5763 isCellEditable : function(colIndex, rowIndex){
5764 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5768 * Returns the editor defined for the cell/column.
5769 * return false or null to disable editing.
5770 * @param {Number} colIndex The column index
5771 * @param {Number} rowIndex The row index
5774 getCellEditor : function(colIndex, rowIndex){
5775 return this.config[colIndex].editor;
5779 * Sets if a column is editable.
5780 * @param {Number} col The column index
5781 * @param {Boolean} editable True if the column is editable
5783 setEditable : function(col, editable){
5784 this.config[col].editable = editable;
5789 * Returns true if the column is hidden.
5790 * @param {Number} colIndex The column index
5793 isHidden : function(colIndex){
5794 return this.config[colIndex].hidden;
5799 * Returns true if the column width cannot be changed
5801 isFixed : function(colIndex){
5802 return this.config[colIndex].fixed;
5806 * Returns true if the column can be resized
5809 isResizable : function(colIndex){
5810 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5813 * Sets if a column is hidden.
5814 * @param {Number} colIndex The column index
5815 * @param {Boolean} hidden True if the column is hidden
5817 setHidden : function(colIndex, hidden){
5818 this.config[colIndex].hidden = hidden;
5819 this.totalWidth = null;
5820 this.fireEvent("hiddenchange", this, colIndex, hidden);
5824 * Sets the editor for a column.
5825 * @param {Number} col The column index
5826 * @param {Object} editor The editor object
5828 setEditor : function(col, editor){
5829 this.config[col].editor = editor;
5833 Roo.grid.ColumnModel.defaultRenderer = function(value)
5835 if(typeof value == "object") {
5838 if(typeof value == "string" && value.length < 1){
5842 return String.format("{0}", value);
5845 // Alias for backwards compatibility
5846 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5849 * Ext JS Library 1.1.1
5850 * Copyright(c) 2006-2007, Ext JS, LLC.
5852 * Originally Released Under LGPL - original licence link has changed is not relivant.
5855 * <script type="text/javascript">
5859 * @class Roo.LoadMask
5860 * A simple utility class for generically masking elements while loading data. If the element being masked has
5861 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5862 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5863 * element's UpdateManager load indicator and will be destroyed after the initial load.
5865 * Create a new LoadMask
5866 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5867 * @param {Object} config The config object
5869 Roo.LoadMask = function(el, config){
5870 this.el = Roo.get(el);
5871 Roo.apply(this, config);
5873 this.store.on('beforeload', this.onBeforeLoad, this);
5874 this.store.on('load', this.onLoad, this);
5875 this.store.on('loadexception', this.onLoadException, this);
5876 this.removeMask = false;
5878 var um = this.el.getUpdateManager();
5879 um.showLoadIndicator = false; // disable the default indicator
5880 um.on('beforeupdate', this.onBeforeLoad, this);
5881 um.on('update', this.onLoad, this);
5882 um.on('failure', this.onLoad, this);
5883 this.removeMask = true;
5887 Roo.LoadMask.prototype = {
5889 * @cfg {Boolean} removeMask
5890 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5891 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5895 * The text to display in a centered loading message box (defaults to 'Loading...')
5899 * @cfg {String} msgCls
5900 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5902 msgCls : 'x-mask-loading',
5905 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5911 * Disables the mask to prevent it from being displayed
5913 disable : function(){
5914 this.disabled = true;
5918 * Enables the mask so that it can be displayed
5920 enable : function(){
5921 this.disabled = false;
5924 onLoadException : function()
5928 if (typeof(arguments[3]) != 'undefined') {
5929 Roo.MessageBox.alert("Error loading",arguments[3]);
5933 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5934 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5941 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5946 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5950 onBeforeLoad : function(){
5952 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5957 destroy : function(){
5959 this.store.un('beforeload', this.onBeforeLoad, this);
5960 this.store.un('load', this.onLoad, this);
5961 this.store.un('loadexception', this.onLoadException, this);
5963 var um = this.el.getUpdateManager();
5964 um.un('beforeupdate', this.onBeforeLoad, this);
5965 um.un('update', this.onLoad, this);
5966 um.un('failure', this.onLoad, this);
5977 * @class Roo.bootstrap.Table
5978 * @extends Roo.bootstrap.Component
5979 * Bootstrap Table class
5980 * @cfg {String} cls table class
5981 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5982 * @cfg {String} bgcolor Specifies the background color for a table
5983 * @cfg {Number} border Specifies whether the table cells should have borders or not
5984 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5985 * @cfg {Number} cellspacing Specifies the space between cells
5986 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5987 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5988 * @cfg {String} sortable Specifies that the table should be sortable
5989 * @cfg {String} summary Specifies a summary of the content of a table
5990 * @cfg {Number} width Specifies the width of a table
5991 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5993 * @cfg {boolean} striped Should the rows be alternative striped
5994 * @cfg {boolean} bordered Add borders to the table
5995 * @cfg {boolean} hover Add hover highlighting
5996 * @cfg {boolean} condensed Format condensed
5997 * @cfg {boolean} responsive Format condensed
5998 * @cfg {Boolean} loadMask (true|false) default false
5999 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6000 * @cfg {Boolean} headerShow (true|false) generate thead, default true
6001 * @cfg {Boolean} rowSelection (true|false) default false
6002 * @cfg {Boolean} cellSelection (true|false) default false
6003 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6004 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
6005 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
6006 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
6010 * Create a new Table
6011 * @param {Object} config The config object
6014 Roo.bootstrap.Table = function(config){
6015 Roo.bootstrap.Table.superclass.constructor.call(this, config);
6020 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6021 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6022 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6023 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6025 this.sm = this.sm || {xtype: 'RowSelectionModel'};
6027 this.sm.grid = this;
6028 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6029 this.sm = this.selModel;
6030 this.sm.xmodule = this.xmodule || false;
6033 if (this.cm && typeof(this.cm.config) == 'undefined') {
6034 this.colModel = new Roo.grid.ColumnModel(this.cm);
6035 this.cm = this.colModel;
6036 this.cm.xmodule = this.xmodule || false;
6039 this.store= Roo.factory(this.store, Roo.data);
6040 this.ds = this.store;
6041 this.ds.xmodule = this.xmodule || false;
6044 if (this.footer && this.store) {
6045 this.footer.dataSource = this.ds;
6046 this.footer = Roo.factory(this.footer);
6053 * Fires when a cell is clicked
6054 * @param {Roo.bootstrap.Table} this
6055 * @param {Roo.Element} el
6056 * @param {Number} rowIndex
6057 * @param {Number} columnIndex
6058 * @param {Roo.EventObject} e
6062 * @event celldblclick
6063 * Fires when a cell is double clicked
6064 * @param {Roo.bootstrap.Table} this
6065 * @param {Roo.Element} el
6066 * @param {Number} rowIndex
6067 * @param {Number} columnIndex
6068 * @param {Roo.EventObject} e
6070 "celldblclick" : true,
6073 * Fires when a row is clicked
6074 * @param {Roo.bootstrap.Table} this
6075 * @param {Roo.Element} el
6076 * @param {Number} rowIndex
6077 * @param {Roo.EventObject} e
6081 * @event rowdblclick
6082 * Fires when a row is double clicked
6083 * @param {Roo.bootstrap.Table} this
6084 * @param {Roo.Element} el
6085 * @param {Number} rowIndex
6086 * @param {Roo.EventObject} e
6088 "rowdblclick" : true,
6091 * Fires when a mouseover occur
6092 * @param {Roo.bootstrap.Table} this
6093 * @param {Roo.Element} el
6094 * @param {Number} rowIndex
6095 * @param {Number} columnIndex
6096 * @param {Roo.EventObject} e
6101 * Fires when a mouseout occur
6102 * @param {Roo.bootstrap.Table} this
6103 * @param {Roo.Element} el
6104 * @param {Number} rowIndex
6105 * @param {Number} columnIndex
6106 * @param {Roo.EventObject} e
6111 * Fires when a row is rendered, so you can change add a style to it.
6112 * @param {Roo.bootstrap.Table} this
6113 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
6117 * @event rowsrendered
6118 * Fires when all the rows have been rendered
6119 * @param {Roo.bootstrap.Table} this
6121 'rowsrendered' : true,
6123 * @event contextmenu
6124 * The raw contextmenu event for the entire grid.
6125 * @param {Roo.EventObject} e
6127 "contextmenu" : true,
6129 * @event rowcontextmenu
6130 * Fires when a row is right clicked
6131 * @param {Roo.bootstrap.Table} this
6132 * @param {Number} rowIndex
6133 * @param {Roo.EventObject} e
6135 "rowcontextmenu" : true,
6137 * @event cellcontextmenu
6138 * Fires when a cell is right clicked
6139 * @param {Roo.bootstrap.Table} this
6140 * @param {Number} rowIndex
6141 * @param {Number} cellIndex
6142 * @param {Roo.EventObject} e
6144 "cellcontextmenu" : true,
6146 * @event headercontextmenu
6147 * Fires when a header is right clicked
6148 * @param {Roo.bootstrap.Table} this
6149 * @param {Number} columnIndex
6150 * @param {Roo.EventObject} e
6152 "headercontextmenu" : true
6156 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
6182 rowSelection : false,
6183 cellSelection : false,
6186 // Roo.Element - the tbody
6188 // Roo.Element - thead element
6191 container: false, // used by gridpanel...
6197 auto_hide_footer : false,
6199 getAutoCreate : function()
6201 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6208 if (this.scrollBody) {
6209 cfg.cls += ' table-body-fixed';
6212 cfg.cls += ' table-striped';
6216 cfg.cls += ' table-hover';
6218 if (this.bordered) {
6219 cfg.cls += ' table-bordered';
6221 if (this.condensed) {
6222 cfg.cls += ' table-condensed';
6224 if (this.responsive) {
6225 cfg.cls += ' table-responsive';
6229 cfg.cls+= ' ' +this.cls;
6232 // this lot should be simplifed...
6245 ].forEach(function(k) {
6253 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6256 if(this.store || this.cm){
6257 if(this.headerShow){
6258 cfg.cn.push(this.renderHeader());
6261 cfg.cn.push(this.renderBody());
6263 if(this.footerShow){
6264 cfg.cn.push(this.renderFooter());
6266 // where does this come from?
6267 //cfg.cls+= ' TableGrid';
6270 return { cn : [ cfg ] };
6273 initEvents : function()
6275 if(!this.store || !this.cm){
6278 if (this.selModel) {
6279 this.selModel.initEvents();
6283 //Roo.log('initEvents with ds!!!!');
6285 this.mainBody = this.el.select('tbody', true).first();
6286 this.mainHead = this.el.select('thead', true).first();
6287 this.mainFoot = this.el.select('tfoot', true).first();
6293 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6294 e.on('click', _this.sort, _this);
6297 this.mainBody.on("click", this.onClick, this);
6298 this.mainBody.on("dblclick", this.onDblClick, this);
6300 // why is this done????? = it breaks dialogs??
6301 //this.parent().el.setStyle('position', 'relative');
6305 this.footer.parentId = this.id;
6306 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6309 this.el.select('tfoot tr td').first().addClass('hide');
6314 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6317 this.store.on('load', this.onLoad, this);
6318 this.store.on('beforeload', this.onBeforeLoad, this);
6319 this.store.on('update', this.onUpdate, this);
6320 this.store.on('add', this.onAdd, this);
6321 this.store.on("clear", this.clear, this);
6323 this.el.on("contextmenu", this.onContextMenu, this);
6325 this.mainBody.on('scroll', this.onBodyScroll, this);
6327 this.cm.on("headerchange", this.onHeaderChange, this);
6329 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6333 onContextMenu : function(e, t)
6335 this.processEvent("contextmenu", e);
6338 processEvent : function(name, e)
6340 if (name != 'touchstart' ) {
6341 this.fireEvent(name, e);
6344 var t = e.getTarget();
6346 var cell = Roo.get(t);
6352 if(cell.findParent('tfoot', false, true)){
6356 if(cell.findParent('thead', false, true)){
6358 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6359 cell = Roo.get(t).findParent('th', false, true);
6361 Roo.log("failed to find th in thead?");
6362 Roo.log(e.getTarget());
6367 var cellIndex = cell.dom.cellIndex;
6369 var ename = name == 'touchstart' ? 'click' : name;
6370 this.fireEvent("header" + ename, this, cellIndex, e);
6375 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6376 cell = Roo.get(t).findParent('td', false, true);
6378 Roo.log("failed to find th in tbody?");
6379 Roo.log(e.getTarget());
6384 var row = cell.findParent('tr', false, true);
6385 var cellIndex = cell.dom.cellIndex;
6386 var rowIndex = row.dom.rowIndex - 1;
6390 this.fireEvent("row" + name, this, rowIndex, e);
6394 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6400 onMouseover : function(e, el)
6402 var cell = Roo.get(el);
6408 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6409 cell = cell.findParent('td', false, true);
6412 var row = cell.findParent('tr', false, true);
6413 var cellIndex = cell.dom.cellIndex;
6414 var rowIndex = row.dom.rowIndex - 1; // start from 0
6416 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6420 onMouseout : function(e, el)
6422 var cell = Roo.get(el);
6428 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6429 cell = cell.findParent('td', false, true);
6432 var row = cell.findParent('tr', false, true);
6433 var cellIndex = cell.dom.cellIndex;
6434 var rowIndex = row.dom.rowIndex - 1; // start from 0
6436 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6440 onClick : function(e, el)
6442 var cell = Roo.get(el);
6444 if(!cell || (!this.cellSelection && !this.rowSelection)){
6448 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6449 cell = cell.findParent('td', false, true);
6452 if(!cell || typeof(cell) == 'undefined'){
6456 var row = cell.findParent('tr', false, true);
6458 if(!row || typeof(row) == 'undefined'){
6462 var cellIndex = cell.dom.cellIndex;
6463 var rowIndex = this.getRowIndex(row);
6465 // why??? - should these not be based on SelectionModel?
6466 if(this.cellSelection){
6467 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6470 if(this.rowSelection){
6471 this.fireEvent('rowclick', this, row, rowIndex, e);
6477 onDblClick : function(e,el)
6479 var cell = Roo.get(el);
6481 if(!cell || (!this.cellSelection && !this.rowSelection)){
6485 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6486 cell = cell.findParent('td', false, true);
6489 if(!cell || typeof(cell) == 'undefined'){
6493 var row = cell.findParent('tr', false, true);
6495 if(!row || typeof(row) == 'undefined'){
6499 var cellIndex = cell.dom.cellIndex;
6500 var rowIndex = this.getRowIndex(row);
6502 if(this.cellSelection){
6503 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6506 if(this.rowSelection){
6507 this.fireEvent('rowdblclick', this, row, rowIndex, e);
6511 sort : function(e,el)
6513 var col = Roo.get(el);
6515 if(!col.hasClass('sortable')){
6519 var sort = col.attr('sort');
6522 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6526 this.store.sortInfo = {field : sort, direction : dir};
6529 Roo.log("calling footer first");
6530 this.footer.onClick('first');
6533 this.store.load({ params : { start : 0 } });
6537 renderHeader : function()
6545 this.totalWidth = 0;
6547 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6549 var config = cm.config[i];
6553 cls : 'x-hcol-' + i,
6555 html: cm.getColumnHeader(i)
6560 if(typeof(config.sortable) != 'undefined' && config.sortable){
6562 c.html = '<i class="glyphicon"></i>' + c.html;
6565 if(typeof(config.lgHeader) != 'undefined'){
6566 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6569 if(typeof(config.mdHeader) != 'undefined'){
6570 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6573 if(typeof(config.smHeader) != 'undefined'){
6574 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6577 if(typeof(config.xsHeader) != 'undefined'){
6578 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6585 if(typeof(config.tooltip) != 'undefined'){
6586 c.tooltip = config.tooltip;
6589 if(typeof(config.colspan) != 'undefined'){
6590 c.colspan = config.colspan;
6593 if(typeof(config.hidden) != 'undefined' && config.hidden){
6594 c.style += ' display:none;';
6597 if(typeof(config.dataIndex) != 'undefined'){
6598 c.sort = config.dataIndex;
6603 if(typeof(config.align) != 'undefined' && config.align.length){
6604 c.style += ' text-align:' + config.align + ';';
6607 if(typeof(config.width) != 'undefined'){
6608 c.style += ' width:' + config.width + 'px;';
6609 this.totalWidth += config.width;
6611 this.totalWidth += 100; // assume minimum of 100 per column?
6614 if(typeof(config.cls) != 'undefined'){
6615 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6618 ['xs','sm','md','lg'].map(function(size){
6620 if(typeof(config[size]) == 'undefined'){
6624 if (!config[size]) { // 0 = hidden
6625 c.cls += ' hidden-' + size;
6629 c.cls += ' col-' + size + '-' + config[size];
6639 renderBody : function()
6649 colspan : this.cm.getColumnCount()
6659 renderFooter : function()
6669 colspan : this.cm.getColumnCount()
6683 // Roo.log('ds onload');
6688 var ds = this.store;
6690 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6691 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6692 if (_this.store.sortInfo) {
6694 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6695 e.select('i', true).addClass(['glyphicon-arrow-up']);
6698 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6699 e.select('i', true).addClass(['glyphicon-arrow-down']);
6704 var tbody = this.mainBody;
6706 if(ds.getCount() > 0){
6707 ds.data.each(function(d,rowIndex){
6708 var row = this.renderRow(cm, ds, rowIndex);
6710 tbody.createChild(row);
6714 if(row.cellObjects.length){
6715 Roo.each(row.cellObjects, function(r){
6716 _this.renderCellObject(r);
6723 var tfoot = this.el.select('tfoot', true).first();
6725 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6727 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6729 var total = this.ds.getTotalCount();
6731 if(this.footer.pageSize < total){
6732 this.mainFoot.show();
6736 Roo.each(this.el.select('tbody td', true).elements, function(e){
6737 e.on('mouseover', _this.onMouseover, _this);
6740 Roo.each(this.el.select('tbody td', true).elements, function(e){
6741 e.on('mouseout', _this.onMouseout, _this);
6743 this.fireEvent('rowsrendered', this);
6749 onUpdate : function(ds,record)
6751 this.refreshRow(record);
6755 onRemove : function(ds, record, index, isUpdate){
6756 if(isUpdate !== true){
6757 this.fireEvent("beforerowremoved", this, index, record);
6759 var bt = this.mainBody.dom;
6761 var rows = this.el.select('tbody > tr', true).elements;
6763 if(typeof(rows[index]) != 'undefined'){
6764 bt.removeChild(rows[index].dom);
6767 // if(bt.rows[index]){
6768 // bt.removeChild(bt.rows[index]);
6771 if(isUpdate !== true){
6772 //this.stripeRows(index);
6773 //this.syncRowHeights(index, index);
6775 this.fireEvent("rowremoved", this, index, record);
6779 onAdd : function(ds, records, rowIndex)
6781 //Roo.log('on Add called');
6782 // - note this does not handle multiple adding very well..
6783 var bt = this.mainBody.dom;
6784 for (var i =0 ; i < records.length;i++) {
6785 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6786 //Roo.log(records[i]);
6787 //Roo.log(this.store.getAt(rowIndex+i));
6788 this.insertRow(this.store, rowIndex + i, false);
6795 refreshRow : function(record){
6796 var ds = this.store, index;
6797 if(typeof record == 'number'){
6799 record = ds.getAt(index);
6801 index = ds.indexOf(record);
6803 this.insertRow(ds, index, true);
6805 this.onRemove(ds, record, index+1, true);
6807 //this.syncRowHeights(index, index);
6809 this.fireEvent("rowupdated", this, index, record);
6812 insertRow : function(dm, rowIndex, isUpdate){
6815 this.fireEvent("beforerowsinserted", this, rowIndex);
6817 //var s = this.getScrollState();
6818 var row = this.renderRow(this.cm, this.store, rowIndex);
6819 // insert before rowIndex..
6820 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6824 if(row.cellObjects.length){
6825 Roo.each(row.cellObjects, function(r){
6826 _this.renderCellObject(r);
6831 this.fireEvent("rowsinserted", this, rowIndex);
6832 //this.syncRowHeights(firstRow, lastRow);
6833 //this.stripeRows(firstRow);
6840 getRowDom : function(rowIndex)
6842 var rows = this.el.select('tbody > tr', true).elements;
6844 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6847 // returns the object tree for a tr..
6850 renderRow : function(cm, ds, rowIndex)
6852 var d = ds.getAt(rowIndex);
6856 cls : 'x-row-' + rowIndex,
6860 var cellObjects = [];
6862 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6863 var config = cm.config[i];
6865 var renderer = cm.getRenderer(i);
6869 if(typeof(renderer) !== 'undefined'){
6870 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6872 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6873 // and are rendered into the cells after the row is rendered - using the id for the element.
6875 if(typeof(value) === 'object'){
6885 rowIndex : rowIndex,
6890 this.fireEvent('rowclass', this, rowcfg);
6894 cls : rowcfg.rowClass + ' x-col-' + i,
6896 html: (typeof(value) === 'object') ? '' : value
6903 if(typeof(config.colspan) != 'undefined'){
6904 td.colspan = config.colspan;
6907 if(typeof(config.hidden) != 'undefined' && config.hidden){
6908 td.style += ' display:none;';
6911 if(typeof(config.align) != 'undefined' && config.align.length){
6912 td.style += ' text-align:' + config.align + ';';
6914 if(typeof(config.valign) != 'undefined' && config.valign.length){
6915 td.style += ' vertical-align:' + config.valign + ';';
6918 if(typeof(config.width) != 'undefined'){
6919 td.style += ' width:' + config.width + 'px;';
6922 if(typeof(config.cursor) != 'undefined'){
6923 td.style += ' cursor:' + config.cursor + ';';
6926 if(typeof(config.cls) != 'undefined'){
6927 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6930 ['xs','sm','md','lg'].map(function(size){
6932 if(typeof(config[size]) == 'undefined'){
6936 if (!config[size]) { // 0 = hidden
6937 td.cls += ' hidden-' + size;
6941 td.cls += ' col-' + size + '-' + config[size];
6949 row.cellObjects = cellObjects;
6957 onBeforeLoad : function()
6966 this.el.select('tbody', true).first().dom.innerHTML = '';
6969 * Show or hide a row.
6970 * @param {Number} rowIndex to show or hide
6971 * @param {Boolean} state hide
6973 setRowVisibility : function(rowIndex, state)
6975 var bt = this.mainBody.dom;
6977 var rows = this.el.select('tbody > tr', true).elements;
6979 if(typeof(rows[rowIndex]) == 'undefined'){
6982 rows[rowIndex].dom.style.display = state ? '' : 'none';
6986 getSelectionModel : function(){
6988 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6990 return this.selModel;
6993 * Render the Roo.bootstrap object from renderder
6995 renderCellObject : function(r)
6999 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7001 var t = r.cfg.render(r.container);
7004 Roo.each(r.cfg.cn, function(c){
7006 container: t.getChildContainer(),
7009 _this.renderCellObject(child);
7014 getRowIndex : function(row)
7018 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7029 * Returns the grid's underlying element = used by panel.Grid
7030 * @return {Element} The element
7032 getGridEl : function(){
7036 * Forces a resize - used by panel.Grid
7037 * @return {Element} The element
7039 autoSize : function()
7041 //var ctr = Roo.get(this.container.dom.parentElement);
7042 var ctr = Roo.get(this.el.dom);
7044 var thd = this.getGridEl().select('thead',true).first();
7045 var tbd = this.getGridEl().select('tbody', true).first();
7046 var tfd = this.getGridEl().select('tfoot', true).first();
7048 var cw = ctr.getWidth();
7052 tbd.setSize(ctr.getWidth(),
7053 ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7055 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7058 cw = Math.max(cw, this.totalWidth);
7059 this.getGridEl().select('tr',true).setWidth(cw);
7060 // resize 'expandable coloumn?
7062 return; // we doe not have a view in this design..
7065 onBodyScroll: function()
7067 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7069 this.mainHead.setStyle({
7070 'position' : 'relative',
7071 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7077 var scrollHeight = this.mainBody.dom.scrollHeight;
7079 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7081 var height = this.mainBody.getHeight();
7083 if(scrollHeight - height == scrollTop) {
7085 var total = this.ds.getTotalCount();
7087 if(this.footer.cursor + this.footer.pageSize < total){
7089 this.footer.ds.load({
7091 start : this.footer.cursor + this.footer.pageSize,
7092 limit : this.footer.pageSize
7102 onHeaderChange : function()
7104 var header = this.renderHeader();
7105 var table = this.el.select('table', true).first();
7107 this.mainHead.remove();
7108 this.mainHead = table.createChild(header, this.mainBody, false);
7111 onHiddenChange : function(colModel, colIndex, hidden)
7113 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7114 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7116 this.CSS.updateRule(thSelector, "display", "");
7117 this.CSS.updateRule(tdSelector, "display", "");
7120 this.CSS.updateRule(thSelector, "display", "none");
7121 this.CSS.updateRule(tdSelector, "display", "none");
7124 this.onHeaderChange();
7128 setColumnWidth: function(col_index, width)
7130 // width = "md-2 xs-2..."
7131 if(!this.colModel.config[col_index]) {
7135 var w = width.split(" ");
7137 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7139 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7142 for(var j = 0; j < w.length; j++) {
7148 var size_cls = w[j].split("-");
7150 if(!Number.isInteger(size_cls[1] * 1)) {
7154 if(!this.colModel.config[col_index][size_cls[0]]) {
7158 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7162 h_row[0].classList.replace(
7163 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7164 "col-"+size_cls[0]+"-"+size_cls[1]
7167 for(var i = 0; i < rows.length; i++) {
7169 var size_cls = w[j].split("-");
7171 if(!Number.isInteger(size_cls[1] * 1)) {
7175 if(!this.colModel.config[col_index][size_cls[0]]) {
7179 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7183 rows[i].classList.replace(
7184 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7185 "col-"+size_cls[0]+"-"+size_cls[1]
7189 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7204 * @class Roo.bootstrap.TableCell
7205 * @extends Roo.bootstrap.Component
7206 * Bootstrap TableCell class
7207 * @cfg {String} html cell contain text
7208 * @cfg {String} cls cell class
7209 * @cfg {String} tag cell tag (td|th) default td
7210 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7211 * @cfg {String} align Aligns the content in a cell
7212 * @cfg {String} axis Categorizes cells
7213 * @cfg {String} bgcolor Specifies the background color of a cell
7214 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7215 * @cfg {Number} colspan Specifies the number of columns a cell should span
7216 * @cfg {String} headers Specifies one or more header cells a cell is related to
7217 * @cfg {Number} height Sets the height of a cell
7218 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7219 * @cfg {Number} rowspan Sets the number of rows a cell should span
7220 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7221 * @cfg {String} valign Vertical aligns the content in a cell
7222 * @cfg {Number} width Specifies the width of a cell
7225 * Create a new TableCell
7226 * @param {Object} config The config object
7229 Roo.bootstrap.TableCell = function(config){
7230 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7233 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
7253 getAutoCreate : function(){
7254 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7274 cfg.align=this.align
7280 cfg.bgcolor=this.bgcolor
7283 cfg.charoff=this.charoff
7286 cfg.colspan=this.colspan
7289 cfg.headers=this.headers
7292 cfg.height=this.height
7295 cfg.nowrap=this.nowrap
7298 cfg.rowspan=this.rowspan
7301 cfg.scope=this.scope
7304 cfg.valign=this.valign
7307 cfg.width=this.width
7326 * @class Roo.bootstrap.TableRow
7327 * @extends Roo.bootstrap.Component
7328 * Bootstrap TableRow class
7329 * @cfg {String} cls row class
7330 * @cfg {String} align Aligns the content in a table row
7331 * @cfg {String} bgcolor Specifies a background color for a table row
7332 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7333 * @cfg {String} valign Vertical aligns the content in a table row
7336 * Create a new TableRow
7337 * @param {Object} config The config object
7340 Roo.bootstrap.TableRow = function(config){
7341 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7344 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
7352 getAutoCreate : function(){
7353 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7363 cfg.align = this.align;
7366 cfg.bgcolor = this.bgcolor;
7369 cfg.charoff = this.charoff;
7372 cfg.valign = this.valign;
7390 * @class Roo.bootstrap.TableBody
7391 * @extends Roo.bootstrap.Component
7392 * Bootstrap TableBody class
7393 * @cfg {String} cls element class
7394 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7395 * @cfg {String} align Aligns the content inside the element
7396 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7397 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7400 * Create a new TableBody
7401 * @param {Object} config The config object
7404 Roo.bootstrap.TableBody = function(config){
7405 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7408 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
7416 getAutoCreate : function(){
7417 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7431 cfg.align = this.align;
7434 cfg.charoff = this.charoff;
7437 cfg.valign = this.valign;
7444 // initEvents : function()
7451 // this.store = Roo.factory(this.store, Roo.data);
7452 // this.store.on('load', this.onLoad, this);
7454 // this.store.load();
7458 // onLoad: function ()
7460 // this.fireEvent('load', this);
7470 * Ext JS Library 1.1.1
7471 * Copyright(c) 2006-2007, Ext JS, LLC.
7473 * Originally Released Under LGPL - original licence link has changed is not relivant.
7476 * <script type="text/javascript">
7479 // as we use this in bootstrap.
7480 Roo.namespace('Roo.form');
7482 * @class Roo.form.Action
7483 * Internal Class used to handle form actions
7485 * @param {Roo.form.BasicForm} el The form element or its id
7486 * @param {Object} config Configuration options
7491 // define the action interface
7492 Roo.form.Action = function(form, options){
7494 this.options = options || {};
7497 * Client Validation Failed
7500 Roo.form.Action.CLIENT_INVALID = 'client';
7502 * Server Validation Failed
7505 Roo.form.Action.SERVER_INVALID = 'server';
7507 * Connect to Server Failed
7510 Roo.form.Action.CONNECT_FAILURE = 'connect';
7512 * Reading Data from Server Failed
7515 Roo.form.Action.LOAD_FAILURE = 'load';
7517 Roo.form.Action.prototype = {
7519 failureType : undefined,
7520 response : undefined,
7524 run : function(options){
7529 success : function(response){
7534 handleResponse : function(response){
7538 // default connection failure
7539 failure : function(response){
7541 this.response = response;
7542 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7543 this.form.afterAction(this, false);
7546 processResponse : function(response){
7547 this.response = response;
7548 if(!response.responseText){
7551 this.result = this.handleResponse(response);
7555 // utility functions used internally
7556 getUrl : function(appendParams){
7557 var url = this.options.url || this.form.url || this.form.el.dom.action;
7559 var p = this.getParams();
7561 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7567 getMethod : function(){
7568 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7571 getParams : function(){
7572 var bp = this.form.baseParams;
7573 var p = this.options.params;
7575 if(typeof p == "object"){
7576 p = Roo.urlEncode(Roo.applyIf(p, bp));
7577 }else if(typeof p == 'string' && bp){
7578 p += '&' + Roo.urlEncode(bp);
7581 p = Roo.urlEncode(bp);
7586 createCallback : function(){
7588 success: this.success,
7589 failure: this.failure,
7591 timeout: (this.form.timeout*1000),
7592 upload: this.form.fileUpload ? this.success : undefined
7597 Roo.form.Action.Submit = function(form, options){
7598 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7601 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7604 haveProgress : false,
7605 uploadComplete : false,
7607 // uploadProgress indicator.
7608 uploadProgress : function()
7610 if (!this.form.progressUrl) {
7614 if (!this.haveProgress) {
7615 Roo.MessageBox.progress("Uploading", "Uploading");
7617 if (this.uploadComplete) {
7618 Roo.MessageBox.hide();
7622 this.haveProgress = true;
7624 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7626 var c = new Roo.data.Connection();
7628 url : this.form.progressUrl,
7633 success : function(req){
7634 //console.log(data);
7638 rdata = Roo.decode(req.responseText)
7640 Roo.log("Invalid data from server..");
7644 if (!rdata || !rdata.success) {
7646 Roo.MessageBox.alert(Roo.encode(rdata));
7649 var data = rdata.data;
7651 if (this.uploadComplete) {
7652 Roo.MessageBox.hide();
7657 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7658 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7661 this.uploadProgress.defer(2000,this);
7664 failure: function(data) {
7665 Roo.log('progress url failed ');
7676 // run get Values on the form, so it syncs any secondary forms.
7677 this.form.getValues();
7679 var o = this.options;
7680 var method = this.getMethod();
7681 var isPost = method == 'POST';
7682 if(o.clientValidation === false || this.form.isValid()){
7684 if (this.form.progressUrl) {
7685 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7686 (new Date() * 1) + '' + Math.random());
7691 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7692 form:this.form.el.dom,
7693 url:this.getUrl(!isPost),
7695 params:isPost ? this.getParams() : null,
7696 isUpload: this.form.fileUpload
7699 this.uploadProgress();
7701 }else if (o.clientValidation !== false){ // client validation failed
7702 this.failureType = Roo.form.Action.CLIENT_INVALID;
7703 this.form.afterAction(this, false);
7707 success : function(response)
7709 this.uploadComplete= true;
7710 if (this.haveProgress) {
7711 Roo.MessageBox.hide();
7715 var result = this.processResponse(response);
7716 if(result === true || result.success){
7717 this.form.afterAction(this, true);
7721 this.form.markInvalid(result.errors);
7722 this.failureType = Roo.form.Action.SERVER_INVALID;
7724 this.form.afterAction(this, false);
7726 failure : function(response)
7728 this.uploadComplete= true;
7729 if (this.haveProgress) {
7730 Roo.MessageBox.hide();
7733 this.response = response;
7734 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7735 this.form.afterAction(this, false);
7738 handleResponse : function(response){
7739 if(this.form.errorReader){
7740 var rs = this.form.errorReader.read(response);
7743 for(var i = 0, len = rs.records.length; i < len; i++) {
7744 var r = rs.records[i];
7748 if(errors.length < 1){
7752 success : rs.success,
7758 ret = Roo.decode(response.responseText);
7762 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7772 Roo.form.Action.Load = function(form, options){
7773 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7774 this.reader = this.form.reader;
7777 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7782 Roo.Ajax.request(Roo.apply(
7783 this.createCallback(), {
7784 method:this.getMethod(),
7785 url:this.getUrl(false),
7786 params:this.getParams()
7790 success : function(response){
7792 var result = this.processResponse(response);
7793 if(result === true || !result.success || !result.data){
7794 this.failureType = Roo.form.Action.LOAD_FAILURE;
7795 this.form.afterAction(this, false);
7798 this.form.clearInvalid();
7799 this.form.setValues(result.data);
7800 this.form.afterAction(this, true);
7803 handleResponse : function(response){
7804 if(this.form.reader){
7805 var rs = this.form.reader.read(response);
7806 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7808 success : rs.success,
7812 return Roo.decode(response.responseText);
7816 Roo.form.Action.ACTION_TYPES = {
7817 'load' : Roo.form.Action.Load,
7818 'submit' : Roo.form.Action.Submit
7827 * @class Roo.bootstrap.Form
7828 * @extends Roo.bootstrap.Component
7829 * Bootstrap Form class
7830 * @cfg {String} method GET | POST (default POST)
7831 * @cfg {String} labelAlign top | left (default top)
7832 * @cfg {String} align left | right - for navbars
7833 * @cfg {Boolean} loadMask load mask when submit (default true)
7838 * @param {Object} config The config object
7842 Roo.bootstrap.Form = function(config){
7844 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7846 Roo.bootstrap.Form.popover.apply();
7850 * @event clientvalidation
7851 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7852 * @param {Form} this
7853 * @param {Boolean} valid true if the form has passed client-side validation
7855 clientvalidation: true,
7857 * @event beforeaction
7858 * Fires before any action is performed. Return false to cancel the action.
7859 * @param {Form} this
7860 * @param {Action} action The action to be performed
7864 * @event actionfailed
7865 * Fires when an action fails.
7866 * @param {Form} this
7867 * @param {Action} action The action that failed
7869 actionfailed : true,
7871 * @event actioncomplete
7872 * Fires when an action is completed.
7873 * @param {Form} this
7874 * @param {Action} action The action that completed
7876 actioncomplete : true
7880 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7883 * @cfg {String} method
7884 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7889 * The URL to use for form actions if one isn't supplied in the action options.
7892 * @cfg {Boolean} fileUpload
7893 * Set to true if this form is a file upload.
7897 * @cfg {Object} baseParams
7898 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7902 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7906 * @cfg {Sting} align (left|right) for navbar forms
7911 activeAction : null,
7914 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7915 * element by passing it or its id or mask the form itself by passing in true.
7918 waitMsgTarget : false,
7923 * @cfg {Boolean} errorMask (true|false) default false
7928 * @cfg {Number} maskOffset Default 100
7933 * @cfg {Boolean} maskBody
7937 getAutoCreate : function(){
7941 method : this.method || 'POST',
7942 id : this.id || Roo.id(),
7945 if (this.parent().xtype.match(/^Nav/)) {
7946 cfg.cls = 'navbar-form navbar-' + this.align;
7950 if (this.labelAlign == 'left' ) {
7951 cfg.cls += ' form-horizontal';
7957 initEvents : function()
7959 this.el.on('submit', this.onSubmit, this);
7960 // this was added as random key presses on the form where triggering form submit.
7961 this.el.on('keypress', function(e) {
7962 if (e.getCharCode() != 13) {
7965 // we might need to allow it for textareas.. and some other items.
7966 // check e.getTarget().
7968 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7972 Roo.log("keypress blocked");
7980 onSubmit : function(e){
7985 * Returns true if client-side validation on the form is successful.
7988 isValid : function(){
7989 var items = this.getItems();
7993 items.each(function(f){
7999 Roo.log('invalid field: ' + f.name);
8003 if(!target && f.el.isVisible(true)){
8009 if(this.errorMask && !valid){
8010 Roo.bootstrap.Form.popover.mask(this, target);
8017 * Returns true if any fields in this form have changed since their original load.
8020 isDirty : function(){
8022 var items = this.getItems();
8023 items.each(function(f){
8033 * Performs a predefined action (submit or load) or custom actions you define on this form.
8034 * @param {String} actionName The name of the action type
8035 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
8036 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8037 * accept other config options):
8039 Property Type Description
8040 ---------------- --------------- ----------------------------------------------------------------------------------
8041 url String The url for the action (defaults to the form's url)
8042 method String The form method to use (defaults to the form's method, or POST if not defined)
8043 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
8044 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
8045 validate the form on the client (defaults to false)
8047 * @return {BasicForm} this
8049 doAction : function(action, options){
8050 if(typeof action == 'string'){
8051 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8053 if(this.fireEvent('beforeaction', this, action) !== false){
8054 this.beforeAction(action);
8055 action.run.defer(100, action);
8061 beforeAction : function(action){
8062 var o = action.options;
8067 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8069 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8072 // not really supported yet.. ??
8074 //if(this.waitMsgTarget === true){
8075 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8076 //}else if(this.waitMsgTarget){
8077 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8078 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8080 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8086 afterAction : function(action, success){
8087 this.activeAction = null;
8088 var o = action.options;
8093 Roo.get(document.body).unmask();
8099 //if(this.waitMsgTarget === true){
8100 // this.el.unmask();
8101 //}else if(this.waitMsgTarget){
8102 // this.waitMsgTarget.unmask();
8104 // Roo.MessageBox.updateProgress(1);
8105 // Roo.MessageBox.hide();
8112 Roo.callback(o.success, o.scope, [this, action]);
8113 this.fireEvent('actioncomplete', this, action);
8117 // failure condition..
8118 // we have a scenario where updates need confirming.
8119 // eg. if a locking scenario exists..
8120 // we look for { errors : { needs_confirm : true }} in the response.
8122 (typeof(action.result) != 'undefined') &&
8123 (typeof(action.result.errors) != 'undefined') &&
8124 (typeof(action.result.errors.needs_confirm) != 'undefined')
8127 Roo.log("not supported yet");
8130 Roo.MessageBox.confirm(
8131 "Change requires confirmation",
8132 action.result.errorMsg,
8137 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
8147 Roo.callback(o.failure, o.scope, [this, action]);
8148 // show an error message if no failed handler is set..
8149 if (!this.hasListener('actionfailed')) {
8150 Roo.log("need to add dialog support");
8152 Roo.MessageBox.alert("Error",
8153 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8154 action.result.errorMsg :
8155 "Saving Failed, please check your entries or try again"
8160 this.fireEvent('actionfailed', this, action);
8165 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8166 * @param {String} id The value to search for
8169 findField : function(id){
8170 var items = this.getItems();
8171 var field = items.get(id);
8173 items.each(function(f){
8174 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8181 return field || null;
8184 * Mark fields in this form invalid in bulk.
8185 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8186 * @return {BasicForm} this
8188 markInvalid : function(errors){
8189 if(errors instanceof Array){
8190 for(var i = 0, len = errors.length; i < len; i++){
8191 var fieldError = errors[i];
8192 var f = this.findField(fieldError.id);
8194 f.markInvalid(fieldError.msg);
8200 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8201 field.markInvalid(errors[id]);
8205 //Roo.each(this.childForms || [], function (f) {
8206 // f.markInvalid(errors);
8213 * Set values for fields in this form in bulk.
8214 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8215 * @return {BasicForm} this
8217 setValues : function(values){
8218 if(values instanceof Array){ // array of objects
8219 for(var i = 0, len = values.length; i < len; i++){
8221 var f = this.findField(v.id);
8223 f.setValue(v.value);
8224 if(this.trackResetOnLoad){
8225 f.originalValue = f.getValue();
8229 }else{ // object hash
8232 if(typeof values[id] != 'function' && (field = this.findField(id))){
8234 if (field.setFromData &&
8236 field.displayField &&
8237 // combos' with local stores can
8238 // be queried via setValue()
8239 // to set their value..
8240 (field.store && !field.store.isLocal)
8244 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8245 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8246 field.setFromData(sd);
8248 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8250 field.setFromData(values);
8253 field.setValue(values[id]);
8257 if(this.trackResetOnLoad){
8258 field.originalValue = field.getValue();
8264 //Roo.each(this.childForms || [], function (f) {
8265 // f.setValues(values);
8272 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8273 * they are returned as an array.
8274 * @param {Boolean} asString
8277 getValues : function(asString){
8278 //if (this.childForms) {
8279 // copy values from the child forms
8280 // Roo.each(this.childForms, function (f) {
8281 // this.setValues(f.getValues());
8287 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8288 if(asString === true){
8291 return Roo.urlDecode(fs);
8295 * Returns the fields in this form as an object with key/value pairs.
8296 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8299 getFieldValues : function(with_hidden)
8301 var items = this.getItems();
8303 items.each(function(f){
8309 var v = f.getValue();
8311 if (f.inputType =='radio') {
8312 if (typeof(ret[f.getName()]) == 'undefined') {
8313 ret[f.getName()] = ''; // empty..
8316 if (!f.el.dom.checked) {
8324 if(f.xtype == 'MoneyField'){
8325 ret[f.currencyName] = f.getCurrency();
8328 // not sure if this supported any more..
8329 if ((typeof(v) == 'object') && f.getRawValue) {
8330 v = f.getRawValue() ; // dates..
8332 // combo boxes where name != hiddenName...
8333 if (f.name !== false && f.name != '' && f.name != f.getName()) {
8334 ret[f.name] = f.getRawValue();
8336 ret[f.getName()] = v;
8343 * Clears all invalid messages in this form.
8344 * @return {BasicForm} this
8346 clearInvalid : function(){
8347 var items = this.getItems();
8349 items.each(function(f){
8358 * @return {BasicForm} this
8361 var items = this.getItems();
8362 items.each(function(f){
8366 Roo.each(this.childForms || [], function (f) {
8374 getItems : function()
8376 var r=new Roo.util.MixedCollection(false, function(o){
8377 return o.id || (o.id = Roo.id());
8379 var iter = function(el) {
8386 Roo.each(el.items,function(e) {
8395 hideFields : function(items)
8397 Roo.each(items, function(i){
8399 var f = this.findField(i);
8410 showFields : function(items)
8412 Roo.each(items, function(i){
8414 var f = this.findField(i);
8427 Roo.apply(Roo.bootstrap.Form, {
8454 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8455 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8456 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8457 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8460 this.maskEl.top.enableDisplayMode("block");
8461 this.maskEl.left.enableDisplayMode("block");
8462 this.maskEl.bottom.enableDisplayMode("block");
8463 this.maskEl.right.enableDisplayMode("block");
8465 this.toolTip = new Roo.bootstrap.Tooltip({
8466 cls : 'roo-form-error-popover',
8468 'left' : ['r-l', [-2,0], 'right'],
8469 'right' : ['l-r', [2,0], 'left'],
8470 'bottom' : ['tl-bl', [0,2], 'top'],
8471 'top' : [ 'bl-tl', [0,-2], 'bottom']
8475 this.toolTip.render(Roo.get(document.body));
8477 this.toolTip.el.enableDisplayMode("block");
8479 Roo.get(document.body).on('click', function(){
8483 Roo.get(document.body).on('touchstart', function(){
8487 this.isApplied = true
8490 mask : function(form, target)
8494 this.target = target;
8496 if(!this.form.errorMask || !target.el){
8500 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8502 Roo.log(scrollable);
8504 var ot = this.target.el.calcOffsetsTo(scrollable);
8506 var scrollTo = ot[1] - this.form.maskOffset;
8508 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8510 scrollable.scrollTo('top', scrollTo);
8512 var box = this.target.el.getBox();
8514 var zIndex = Roo.bootstrap.Modal.zIndex++;
8517 this.maskEl.top.setStyle('position', 'absolute');
8518 this.maskEl.top.setStyle('z-index', zIndex);
8519 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8520 this.maskEl.top.setLeft(0);
8521 this.maskEl.top.setTop(0);
8522 this.maskEl.top.show();
8524 this.maskEl.left.setStyle('position', 'absolute');
8525 this.maskEl.left.setStyle('z-index', zIndex);
8526 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8527 this.maskEl.left.setLeft(0);
8528 this.maskEl.left.setTop(box.y - this.padding);
8529 this.maskEl.left.show();
8531 this.maskEl.bottom.setStyle('position', 'absolute');
8532 this.maskEl.bottom.setStyle('z-index', zIndex);
8533 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8534 this.maskEl.bottom.setLeft(0);
8535 this.maskEl.bottom.setTop(box.bottom + this.padding);
8536 this.maskEl.bottom.show();
8538 this.maskEl.right.setStyle('position', 'absolute');
8539 this.maskEl.right.setStyle('z-index', zIndex);
8540 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8541 this.maskEl.right.setLeft(box.right + this.padding);
8542 this.maskEl.right.setTop(box.y - this.padding);
8543 this.maskEl.right.show();
8545 this.toolTip.bindEl = this.target.el;
8547 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8549 var tip = this.target.blankText;
8551 if(this.target.getValue() !== '' ) {
8553 if (this.target.invalidText.length) {
8554 tip = this.target.invalidText;
8555 } else if (this.target.regexText.length){
8556 tip = this.target.regexText;
8560 this.toolTip.show(tip);
8562 this.intervalID = window.setInterval(function() {
8563 Roo.bootstrap.Form.popover.unmask();
8566 window.onwheel = function(){ return false;};
8568 (function(){ this.isMasked = true; }).defer(500, this);
8574 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8578 this.maskEl.top.setStyle('position', 'absolute');
8579 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8580 this.maskEl.top.hide();
8582 this.maskEl.left.setStyle('position', 'absolute');
8583 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8584 this.maskEl.left.hide();
8586 this.maskEl.bottom.setStyle('position', 'absolute');
8587 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8588 this.maskEl.bottom.hide();
8590 this.maskEl.right.setStyle('position', 'absolute');
8591 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8592 this.maskEl.right.hide();
8594 this.toolTip.hide();
8596 this.toolTip.el.hide();
8598 window.onwheel = function(){ return true;};
8600 if(this.intervalID){
8601 window.clearInterval(this.intervalID);
8602 this.intervalID = false;
8605 this.isMasked = false;
8615 * Ext JS Library 1.1.1
8616 * Copyright(c) 2006-2007, Ext JS, LLC.
8618 * Originally Released Under LGPL - original licence link has changed is not relivant.
8621 * <script type="text/javascript">
8624 * @class Roo.form.VTypes
8625 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8628 Roo.form.VTypes = function(){
8629 // closure these in so they are only created once.
8630 var alpha = /^[a-zA-Z_]+$/;
8631 var alphanum = /^[a-zA-Z0-9_]+$/;
8632 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8633 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8635 // All these messages and functions are configurable
8638 * The function used to validate email addresses
8639 * @param {String} value The email address
8641 'email' : function(v){
8642 return email.test(v);
8645 * The error text to display when the email validation function returns false
8648 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8650 * The keystroke filter mask to be applied on email input
8653 'emailMask' : /[a-z0-9_\.\-@]/i,
8656 * The function used to validate URLs
8657 * @param {String} value The URL
8659 'url' : function(v){
8663 * The error text to display when the url validation function returns false
8666 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8669 * The function used to validate alpha values
8670 * @param {String} value The value
8672 'alpha' : function(v){
8673 return alpha.test(v);
8676 * The error text to display when the alpha validation function returns false
8679 'alphaText' : 'This field should only contain letters and _',
8681 * The keystroke filter mask to be applied on alpha input
8684 'alphaMask' : /[a-z_]/i,
8687 * The function used to validate alphanumeric values
8688 * @param {String} value The value
8690 'alphanum' : function(v){
8691 return alphanum.test(v);
8694 * The error text to display when the alphanumeric validation function returns false
8697 'alphanumText' : 'This field should only contain letters, numbers and _',
8699 * The keystroke filter mask to be applied on alphanumeric input
8702 'alphanumMask' : /[a-z0-9_]/i
8712 * @class Roo.bootstrap.Input
8713 * @extends Roo.bootstrap.Component
8714 * Bootstrap Input class
8715 * @cfg {Boolean} disabled is it disabled
8716 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8717 * @cfg {String} name name of the input
8718 * @cfg {string} fieldLabel - the label associated
8719 * @cfg {string} placeholder - placeholder to put in text.
8720 * @cfg {string} before - input group add on before
8721 * @cfg {string} after - input group add on after
8722 * @cfg {string} size - (lg|sm) or leave empty..
8723 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8724 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8725 * @cfg {Number} md colspan out of 12 for computer-sized screens
8726 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8727 * @cfg {string} value default value of the input
8728 * @cfg {Number} labelWidth set the width of label
8729 * @cfg {Number} labellg set the width of label (1-12)
8730 * @cfg {Number} labelmd set the width of label (1-12)
8731 * @cfg {Number} labelsm set the width of label (1-12)
8732 * @cfg {Number} labelxs set the width of label (1-12)
8733 * @cfg {String} labelAlign (top|left)
8734 * @cfg {Boolean} readOnly Specifies that the field should be read-only
8735 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8736 * @cfg {String} indicatorpos (left|right) default left
8737 * @cfg {String} capture (user|camera) use for file input only. (default empty)
8738 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8740 * @cfg {String} align (left|center|right) Default left
8741 * @cfg {Boolean} forceFeedback (true|false) Default false
8744 * Create a new Input
8745 * @param {Object} config The config object
8748 Roo.bootstrap.Input = function(config){
8750 Roo.bootstrap.Input.superclass.constructor.call(this, config);
8755 * Fires when this field receives input focus.
8756 * @param {Roo.form.Field} this
8761 * Fires when this field loses input focus.
8762 * @param {Roo.form.Field} this
8767 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8768 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8769 * @param {Roo.form.Field} this
8770 * @param {Roo.EventObject} e The event object
8775 * Fires just before the field blurs if the field value has changed.
8776 * @param {Roo.form.Field} this
8777 * @param {Mixed} newValue The new value
8778 * @param {Mixed} oldValue The original value
8783 * Fires after the field has been marked as invalid.
8784 * @param {Roo.form.Field} this
8785 * @param {String} msg The validation message
8790 * Fires after the field has been validated with no errors.
8791 * @param {Roo.form.Field} this
8796 * Fires after the key up
8797 * @param {Roo.form.Field} this
8798 * @param {Roo.EventObject} e The event Object
8804 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
8806 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8807 automatic validation (defaults to "keyup").
8809 validationEvent : "keyup",
8811 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8813 validateOnBlur : true,
8815 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8817 validationDelay : 250,
8819 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8821 focusClass : "x-form-focus", // not needed???
8825 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8827 invalidClass : "has-warning",
8830 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8832 validClass : "has-success",
8835 * @cfg {Boolean} hasFeedback (true|false) default true
8840 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8842 invalidFeedbackClass : "glyphicon-warning-sign",
8845 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8847 validFeedbackClass : "glyphicon-ok",
8850 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8852 selectOnFocus : false,
8855 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8859 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8864 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8866 disableKeyFilter : false,
8869 * @cfg {Boolean} disabled True to disable the field (defaults to false).
8873 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8877 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8879 blankText : "Please complete this mandatory field",
8882 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8886 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8888 maxLength : Number.MAX_VALUE,
8890 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8892 minLengthText : "The minimum length for this field is {0}",
8894 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8896 maxLengthText : "The maximum length for this field is {0}",
8900 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8901 * If available, this function will be called only after the basic validators all return true, and will be passed the
8902 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8906 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8907 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8908 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
8912 * @cfg {String} regexText -- Depricated - use Invalid Text
8917 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8923 autocomplete: false,
8942 formatedValue : false,
8943 forceFeedback : false,
8945 indicatorpos : 'left',
8955 parentLabelAlign : function()
8958 while (parent.parent()) {
8959 parent = parent.parent();
8960 if (typeof(parent.labelAlign) !='undefined') {
8961 return parent.labelAlign;
8968 getAutoCreate : function()
8970 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8976 if(this.inputType != 'hidden'){
8977 cfg.cls = 'form-group' //input-group
8983 type : this.inputType,
8985 cls : 'form-control',
8986 placeholder : this.placeholder || '',
8987 autocomplete : this.autocomplete || 'new-password'
8990 if(this.capture.length){
8991 input.capture = this.capture;
8994 if(this.accept.length){
8995 input.accept = this.accept + "/*";
8999 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9002 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9003 input.maxLength = this.maxLength;
9006 if (this.disabled) {
9007 input.disabled=true;
9010 if (this.readOnly) {
9011 input.readonly=true;
9015 input.name = this.name;
9019 input.cls += ' input-' + this.size;
9023 ['xs','sm','md','lg'].map(function(size){
9024 if (settings[size]) {
9025 cfg.cls += ' col-' + size + '-' + settings[size];
9029 var inputblock = input;
9033 cls: 'glyphicon form-control-feedback'
9036 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9039 cls : 'has-feedback',
9047 if (this.before || this.after) {
9050 cls : 'input-group',
9054 if (this.before && typeof(this.before) == 'string') {
9056 inputblock.cn.push({
9058 cls : 'roo-input-before input-group-addon',
9062 if (this.before && typeof(this.before) == 'object') {
9063 this.before = Roo.factory(this.before);
9065 inputblock.cn.push({
9067 cls : 'roo-input-before input-group-' +
9068 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9072 inputblock.cn.push(input);
9074 if (this.after && typeof(this.after) == 'string') {
9075 inputblock.cn.push({
9077 cls : 'roo-input-after input-group-addon',
9081 if (this.after && typeof(this.after) == 'object') {
9082 this.after = Roo.factory(this.after);
9084 inputblock.cn.push({
9086 cls : 'roo-input-after input-group-' +
9087 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9091 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9092 inputblock.cls += ' has-feedback';
9093 inputblock.cn.push(feedback);
9097 if (align ==='left' && this.fieldLabel.length) {
9099 cfg.cls += ' roo-form-group-label-left';
9104 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9105 tooltip : 'This field is required'
9110 cls : 'control-label',
9111 html : this.fieldLabel
9122 var labelCfg = cfg.cn[1];
9123 var contentCfg = cfg.cn[2];
9125 if(this.indicatorpos == 'right'){
9130 cls : 'control-label',
9134 html : this.fieldLabel
9138 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9139 tooltip : 'This field is required'
9152 labelCfg = cfg.cn[0];
9153 contentCfg = cfg.cn[1];
9157 if(this.labelWidth > 12){
9158 labelCfg.style = "width: " + this.labelWidth + 'px';
9161 if(this.labelWidth < 13 && this.labelmd == 0){
9162 this.labelmd = this.labelWidth;
9165 if(this.labellg > 0){
9166 labelCfg.cls += ' col-lg-' + this.labellg;
9167 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9170 if(this.labelmd > 0){
9171 labelCfg.cls += ' col-md-' + this.labelmd;
9172 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9175 if(this.labelsm > 0){
9176 labelCfg.cls += ' col-sm-' + this.labelsm;
9177 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9180 if(this.labelxs > 0){
9181 labelCfg.cls += ' col-xs-' + this.labelxs;
9182 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9186 } else if ( this.fieldLabel.length) {
9191 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9192 tooltip : 'This field is required'
9196 //cls : 'input-group-addon',
9197 html : this.fieldLabel
9205 if(this.indicatorpos == 'right'){
9210 //cls : 'input-group-addon',
9211 html : this.fieldLabel
9216 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9217 tooltip : 'This field is required'
9237 if (this.parentType === 'Navbar' && this.parent().bar) {
9238 cfg.cls += ' navbar-form';
9241 if (this.parentType === 'NavGroup') {
9242 cfg.cls += ' navbar-form';
9250 * return the real input element.
9252 inputEl: function ()
9254 return this.el.select('input.form-control',true).first();
9257 tooltipEl : function()
9259 return this.inputEl();
9262 indicatorEl : function()
9264 var indicator = this.el.select('i.roo-required-indicator',true).first();
9274 setDisabled : function(v)
9276 var i = this.inputEl().dom;
9278 i.removeAttribute('disabled');
9282 i.setAttribute('disabled','true');
9284 initEvents : function()
9287 this.inputEl().on("keydown" , this.fireKey, this);
9288 this.inputEl().on("focus", this.onFocus, this);
9289 this.inputEl().on("blur", this.onBlur, this);
9291 this.inputEl().relayEvent('keyup', this);
9293 this.indicator = this.indicatorEl();
9296 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
9299 // reference to original value for reset
9300 this.originalValue = this.getValue();
9301 //Roo.form.TextField.superclass.initEvents.call(this);
9302 if(this.validationEvent == 'keyup'){
9303 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9304 this.inputEl().on('keyup', this.filterValidation, this);
9306 else if(this.validationEvent !== false){
9307 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9310 if(this.selectOnFocus){
9311 this.on("focus", this.preFocus, this);
9314 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9315 this.inputEl().on("keypress", this.filterKeys, this);
9317 this.inputEl().relayEvent('keypress', this);
9320 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
9321 this.el.on("click", this.autoSize, this);
9324 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9325 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9328 if (typeof(this.before) == 'object') {
9329 this.before.render(this.el.select('.roo-input-before',true).first());
9331 if (typeof(this.after) == 'object') {
9332 this.after.render(this.el.select('.roo-input-after',true).first());
9335 this.inputEl().on('change', this.onChange, this);
9338 filterValidation : function(e){
9339 if(!e.isNavKeyPress()){
9340 this.validationTask.delay(this.validationDelay);
9344 * Validates the field value
9345 * @return {Boolean} True if the value is valid, else false
9347 validate : function(){
9348 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9349 if(this.disabled || this.validateValue(this.getRawValue())){
9360 * Validates a value according to the field's validation rules and marks the field as invalid
9361 * if the validation fails
9362 * @param {Mixed} value The value to validate
9363 * @return {Boolean} True if the value is valid, else false
9365 validateValue : function(value)
9367 if(this.getVisibilityEl().hasClass('hidden')){
9371 if(value.length < 1) { // if it's blank
9372 if(this.allowBlank){
9378 if(value.length < this.minLength){
9381 if(value.length > this.maxLength){
9385 var vt = Roo.form.VTypes;
9386 if(!vt[this.vtype](value, this)){
9390 if(typeof this.validator == "function"){
9391 var msg = this.validator(value);
9395 if (typeof(msg) == 'string') {
9396 this.invalidText = msg;
9400 if(this.regex && !this.regex.test(value)){
9408 fireKey : function(e){
9409 //Roo.log('field ' + e.getKey());
9410 if(e.isNavKeyPress()){
9411 this.fireEvent("specialkey", this, e);
9414 focus : function (selectText){
9416 this.inputEl().focus();
9417 if(selectText === true){
9418 this.inputEl().dom.select();
9424 onFocus : function(){
9425 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9426 // this.el.addClass(this.focusClass);
9429 this.hasFocus = true;
9430 this.startValue = this.getValue();
9431 this.fireEvent("focus", this);
9435 beforeBlur : Roo.emptyFn,
9439 onBlur : function(){
9441 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9442 //this.el.removeClass(this.focusClass);
9444 this.hasFocus = false;
9445 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9448 var v = this.getValue();
9449 if(String(v) !== String(this.startValue)){
9450 this.fireEvent('change', this, v, this.startValue);
9452 this.fireEvent("blur", this);
9455 onChange : function(e)
9457 var v = this.getValue();
9458 if(String(v) !== String(this.startValue)){
9459 this.fireEvent('change', this, v, this.startValue);
9465 * Resets the current field value to the originally loaded value and clears any validation messages
9468 this.setValue(this.originalValue);
9472 * Returns the name of the field
9473 * @return {Mixed} name The name field
9475 getName: function(){
9479 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
9480 * @return {Mixed} value The field value
9482 getValue : function(){
9484 var v = this.inputEl().getValue();
9489 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
9490 * @return {Mixed} value The field value
9492 getRawValue : function(){
9493 var v = this.inputEl().getValue();
9499 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
9500 * @param {Mixed} value The value to set
9502 setRawValue : function(v){
9503 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9506 selectText : function(start, end){
9507 var v = this.getRawValue();
9509 start = start === undefined ? 0 : start;
9510 end = end === undefined ? v.length : end;
9511 var d = this.inputEl().dom;
9512 if(d.setSelectionRange){
9513 d.setSelectionRange(start, end);
9514 }else if(d.createTextRange){
9515 var range = d.createTextRange();
9516 range.moveStart("character", start);
9517 range.moveEnd("character", v.length-end);
9524 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
9525 * @param {Mixed} value The value to set
9527 setValue : function(v){
9530 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9536 processValue : function(value){
9537 if(this.stripCharsRe){
9538 var newValue = value.replace(this.stripCharsRe, '');
9539 if(newValue !== value){
9540 this.setRawValue(newValue);
9547 preFocus : function(){
9549 if(this.selectOnFocus){
9550 this.inputEl().dom.select();
9553 filterKeys : function(e){
9555 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9558 var c = e.getCharCode(), cc = String.fromCharCode(c);
9559 if(Roo.isIE && (e.isSpecialKey() || !cc)){
9562 if(!this.maskRe.test(cc)){
9567 * Clear any invalid styles/messages for this field
9569 clearInvalid : function(){
9571 if(!this.el || this.preventMark){ // not rendered
9576 this.el.removeClass(this.invalidClass);
9578 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9580 var feedback = this.el.select('.form-control-feedback', true).first();
9583 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9589 this.indicator.removeClass('visible');
9590 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9593 this.fireEvent('valid', this);
9597 * Mark this field as valid
9599 markValid : function()
9601 if(!this.el || this.preventMark){ // not rendered...
9605 this.el.removeClass([this.invalidClass, this.validClass]);
9607 var feedback = this.el.select('.form-control-feedback', true).first();
9610 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9614 this.indicator.removeClass('visible');
9615 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9622 if(this.allowBlank && !this.getRawValue().length){
9626 this.el.addClass(this.validClass);
9628 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9630 var feedback = this.el.select('.form-control-feedback', true).first();
9633 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9634 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9639 this.fireEvent('valid', this);
9643 * Mark this field as invalid
9644 * @param {String} msg The validation message
9646 markInvalid : function(msg)
9648 if(!this.el || this.preventMark){ // not rendered
9652 this.el.removeClass([this.invalidClass, this.validClass]);
9654 var feedback = this.el.select('.form-control-feedback', true).first();
9657 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9664 if(this.allowBlank && !this.getRawValue().length){
9669 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9670 this.indicator.addClass('visible');
9673 this.el.addClass(this.invalidClass);
9675 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9677 var feedback = this.el.select('.form-control-feedback', true).first();
9680 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9682 if(this.getValue().length || this.forceFeedback){
9683 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9690 this.fireEvent('invalid', this, msg);
9693 SafariOnKeyDown : function(event)
9695 // this is a workaround for a password hang bug on chrome/ webkit.
9696 if (this.inputEl().dom.type != 'password') {
9700 var isSelectAll = false;
9702 if(this.inputEl().dom.selectionEnd > 0){
9703 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9705 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9706 event.preventDefault();
9711 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9713 event.preventDefault();
9714 // this is very hacky as keydown always get's upper case.
9716 var cc = String.fromCharCode(event.getCharCode());
9717 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
9721 adjustWidth : function(tag, w){
9722 tag = tag.toLowerCase();
9723 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9724 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9728 if(tag == 'textarea'){
9731 }else if(Roo.isOpera){
9735 if(tag == 'textarea'){
9743 setFieldLabel : function(v)
9750 var ar = this.el.select('label > span',true);
9752 if (ar.elements.length) {
9753 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9754 this.fieldLabel = v;
9758 var br = this.el.select('label',true);
9760 if(br.elements.length) {
9761 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9762 this.fieldLabel = v;
9766 Roo.log('Cannot Found any of label > span || label in input');
9770 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9771 this.fieldLabel = v;
9786 * @class Roo.bootstrap.TextArea
9787 * @extends Roo.bootstrap.Input
9788 * Bootstrap TextArea class
9789 * @cfg {Number} cols Specifies the visible width of a text area
9790 * @cfg {Number} rows Specifies the visible number of lines in a text area
9791 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9792 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9793 * @cfg {string} html text
9796 * Create a new TextArea
9797 * @param {Object} config The config object
9800 Roo.bootstrap.TextArea = function(config){
9801 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9805 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
9815 getAutoCreate : function(){
9817 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9823 if(this.inputType != 'hidden'){
9824 cfg.cls = 'form-group' //input-group
9832 value : this.value || '',
9833 html: this.html || '',
9834 cls : 'form-control',
9835 placeholder : this.placeholder || ''
9839 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9840 input.maxLength = this.maxLength;
9844 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9848 input.cols = this.cols;
9851 if (this.readOnly) {
9852 input.readonly = true;
9856 input.name = this.name;
9860 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9864 ['xs','sm','md','lg'].map(function(size){
9865 if (settings[size]) {
9866 cfg.cls += ' col-' + size + '-' + settings[size];
9870 var inputblock = input;
9872 if(this.hasFeedback && !this.allowBlank){
9876 cls: 'glyphicon form-control-feedback'
9880 cls : 'has-feedback',
9889 if (this.before || this.after) {
9892 cls : 'input-group',
9896 inputblock.cn.push({
9898 cls : 'input-group-addon',
9903 inputblock.cn.push(input);
9905 if(this.hasFeedback && !this.allowBlank){
9906 inputblock.cls += ' has-feedback';
9907 inputblock.cn.push(feedback);
9911 inputblock.cn.push({
9913 cls : 'input-group-addon',
9920 if (align ==='left' && this.fieldLabel.length) {
9925 cls : 'control-label',
9926 html : this.fieldLabel
9937 if(this.labelWidth > 12){
9938 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9941 if(this.labelWidth < 13 && this.labelmd == 0){
9942 this.labelmd = this.labelWidth;
9945 if(this.labellg > 0){
9946 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9947 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9950 if(this.labelmd > 0){
9951 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9952 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9955 if(this.labelsm > 0){
9956 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9957 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9960 if(this.labelxs > 0){
9961 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9962 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9965 } else if ( this.fieldLabel.length) {
9970 //cls : 'input-group-addon',
9971 html : this.fieldLabel
9989 if (this.disabled) {
9990 input.disabled=true;
9997 * return the real textarea element.
9999 inputEl: function ()
10001 return this.el.select('textarea.form-control',true).first();
10005 * Clear any invalid styles/messages for this field
10007 clearInvalid : function()
10010 if(!this.el || this.preventMark){ // not rendered
10014 var label = this.el.select('label', true).first();
10015 var icon = this.el.select('i.fa-star', true).first();
10021 this.el.removeClass(this.invalidClass);
10023 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10025 var feedback = this.el.select('.form-control-feedback', true).first();
10028 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10033 this.fireEvent('valid', this);
10037 * Mark this field as valid
10039 markValid : function()
10041 if(!this.el || this.preventMark){ // not rendered
10045 this.el.removeClass([this.invalidClass, this.validClass]);
10047 var feedback = this.el.select('.form-control-feedback', true).first();
10050 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10053 if(this.disabled || this.allowBlank){
10057 var label = this.el.select('label', true).first();
10058 var icon = this.el.select('i.fa-star', true).first();
10064 this.el.addClass(this.validClass);
10066 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10068 var feedback = this.el.select('.form-control-feedback', true).first();
10071 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10072 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10077 this.fireEvent('valid', this);
10081 * Mark this field as invalid
10082 * @param {String} msg The validation message
10084 markInvalid : function(msg)
10086 if(!this.el || this.preventMark){ // not rendered
10090 this.el.removeClass([this.invalidClass, this.validClass]);
10092 var feedback = this.el.select('.form-control-feedback', true).first();
10095 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10098 if(this.disabled || this.allowBlank){
10102 var label = this.el.select('label', true).first();
10103 var icon = this.el.select('i.fa-star', true).first();
10105 if(!this.getValue().length && label && !icon){
10106 this.el.createChild({
10108 cls : 'text-danger fa fa-lg fa-star',
10109 tooltip : 'This field is required',
10110 style : 'margin-right:5px;'
10114 this.el.addClass(this.invalidClass);
10116 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10118 var feedback = this.el.select('.form-control-feedback', true).first();
10121 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10123 if(this.getValue().length || this.forceFeedback){
10124 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10131 this.fireEvent('invalid', this, msg);
10139 * trigger field - base class for combo..
10144 * @class Roo.bootstrap.TriggerField
10145 * @extends Roo.bootstrap.Input
10146 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10147 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10148 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10149 * for which you can provide a custom implementation. For example:
10151 var trigger = new Roo.bootstrap.TriggerField();
10152 trigger.onTriggerClick = myTriggerFn;
10153 trigger.applyTo('my-field');
10156 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10157 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10158 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
10159 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10160 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10163 * Create a new TriggerField.
10164 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10165 * to the base TextField)
10167 Roo.bootstrap.TriggerField = function(config){
10168 this.mimicing = false;
10169 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10172 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
10174 * @cfg {String} triggerClass A CSS class to apply to the trigger
10177 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10182 * @cfg {Boolean} removable (true|false) special filter default false
10186 /** @cfg {Boolean} grow @hide */
10187 /** @cfg {Number} growMin @hide */
10188 /** @cfg {Number} growMax @hide */
10194 autoSize: Roo.emptyFn,
10198 deferHeight : true,
10201 actionMode : 'wrap',
10206 getAutoCreate : function(){
10208 var align = this.labelAlign || this.parentLabelAlign();
10213 cls: 'form-group' //input-group
10220 type : this.inputType,
10221 cls : 'form-control',
10222 autocomplete: 'new-password',
10223 placeholder : this.placeholder || ''
10227 input.name = this.name;
10230 input.cls += ' input-' + this.size;
10233 if (this.disabled) {
10234 input.disabled=true;
10237 var inputblock = input;
10239 if(this.hasFeedback && !this.allowBlank){
10243 cls: 'glyphicon form-control-feedback'
10246 if(this.removable && !this.editable && !this.tickable){
10248 cls : 'has-feedback',
10254 cls : 'roo-combo-removable-btn close'
10261 cls : 'has-feedback',
10270 if(this.removable && !this.editable && !this.tickable){
10272 cls : 'roo-removable',
10278 cls : 'roo-combo-removable-btn close'
10285 if (this.before || this.after) {
10288 cls : 'input-group',
10292 inputblock.cn.push({
10294 cls : 'input-group-addon',
10299 inputblock.cn.push(input);
10301 if(this.hasFeedback && !this.allowBlank){
10302 inputblock.cls += ' has-feedback';
10303 inputblock.cn.push(feedback);
10307 inputblock.cn.push({
10309 cls : 'input-group-addon',
10322 cls: 'form-hidden-field'
10336 cls: 'form-hidden-field'
10340 cls: 'roo-select2-choices',
10344 cls: 'roo-select2-search-field',
10357 cls: 'roo-select2-container input-group',
10362 // cls: 'typeahead typeahead-long dropdown-menu',
10363 // style: 'display:none'
10368 if(!this.multiple && this.showToggleBtn){
10374 if (this.caret != false) {
10377 cls: 'fa fa-' + this.caret
10384 cls : 'input-group-addon btn dropdown-toggle',
10389 cls: 'combobox-clear',
10403 combobox.cls += ' roo-select2-container-multi';
10406 if (align ==='left' && this.fieldLabel.length) {
10408 cfg.cls += ' roo-form-group-label-left';
10413 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10414 tooltip : 'This field is required'
10419 cls : 'control-label',
10420 html : this.fieldLabel
10432 var labelCfg = cfg.cn[1];
10433 var contentCfg = cfg.cn[2];
10435 if(this.indicatorpos == 'right'){
10440 cls : 'control-label',
10444 html : this.fieldLabel
10448 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10449 tooltip : 'This field is required'
10462 labelCfg = cfg.cn[0];
10463 contentCfg = cfg.cn[1];
10466 if(this.labelWidth > 12){
10467 labelCfg.style = "width: " + this.labelWidth + 'px';
10470 if(this.labelWidth < 13 && this.labelmd == 0){
10471 this.labelmd = this.labelWidth;
10474 if(this.labellg > 0){
10475 labelCfg.cls += ' col-lg-' + this.labellg;
10476 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10479 if(this.labelmd > 0){
10480 labelCfg.cls += ' col-md-' + this.labelmd;
10481 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10484 if(this.labelsm > 0){
10485 labelCfg.cls += ' col-sm-' + this.labelsm;
10486 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10489 if(this.labelxs > 0){
10490 labelCfg.cls += ' col-xs-' + this.labelxs;
10491 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10494 } else if ( this.fieldLabel.length) {
10495 // Roo.log(" label");
10499 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10500 tooltip : 'This field is required'
10504 //cls : 'input-group-addon',
10505 html : this.fieldLabel
10513 if(this.indicatorpos == 'right'){
10521 html : this.fieldLabel
10525 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10526 tooltip : 'This field is required'
10539 // Roo.log(" no label && no align");
10546 ['xs','sm','md','lg'].map(function(size){
10547 if (settings[size]) {
10548 cfg.cls += ' col-' + size + '-' + settings[size];
10559 onResize : function(w, h){
10560 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10561 // if(typeof w == 'number'){
10562 // var x = w - this.trigger.getWidth();
10563 // this.inputEl().setWidth(this.adjustWidth('input', x));
10564 // this.trigger.setStyle('left', x+'px');
10569 adjustSize : Roo.BoxComponent.prototype.adjustSize,
10572 getResizeEl : function(){
10573 return this.inputEl();
10577 getPositionEl : function(){
10578 return this.inputEl();
10582 alignErrorIcon : function(){
10583 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10587 initEvents : function(){
10591 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10592 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10593 if(!this.multiple && this.showToggleBtn){
10594 this.trigger = this.el.select('span.dropdown-toggle',true).first();
10595 if(this.hideTrigger){
10596 this.trigger.setDisplayed(false);
10598 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10602 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10605 if(this.removable && !this.editable && !this.tickable){
10606 var close = this.closeTriggerEl();
10609 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10610 close.on('click', this.removeBtnClick, this, close);
10614 //this.trigger.addClassOnOver('x-form-trigger-over');
10615 //this.trigger.addClassOnClick('x-form-trigger-click');
10618 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10622 closeTriggerEl : function()
10624 var close = this.el.select('.roo-combo-removable-btn', true).first();
10625 return close ? close : false;
10628 removeBtnClick : function(e, h, el)
10630 e.preventDefault();
10632 if(this.fireEvent("remove", this) !== false){
10634 this.fireEvent("afterremove", this)
10638 createList : function()
10640 this.list = Roo.get(document.body).createChild({
10642 cls: 'typeahead typeahead-long dropdown-menu',
10643 style: 'display:none'
10646 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10651 initTrigger : function(){
10656 onDestroy : function(){
10658 this.trigger.removeAllListeners();
10659 // this.trigger.remove();
10662 // this.wrap.remove();
10664 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10668 onFocus : function(){
10669 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10671 if(!this.mimicing){
10672 this.wrap.addClass('x-trigger-wrap-focus');
10673 this.mimicing = true;
10674 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10675 if(this.monitorTab){
10676 this.el.on("keydown", this.checkTab, this);
10683 checkTab : function(e){
10684 if(e.getKey() == e.TAB){
10685 this.triggerBlur();
10690 onBlur : function(){
10695 mimicBlur : function(e, t){
10697 if(!this.wrap.contains(t) && this.validateBlur()){
10698 this.triggerBlur();
10704 triggerBlur : function(){
10705 this.mimicing = false;
10706 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10707 if(this.monitorTab){
10708 this.el.un("keydown", this.checkTab, this);
10710 //this.wrap.removeClass('x-trigger-wrap-focus');
10711 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10715 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10716 validateBlur : function(e, t){
10721 onDisable : function(){
10722 this.inputEl().dom.disabled = true;
10723 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10725 // this.wrap.addClass('x-item-disabled');
10730 onEnable : function(){
10731 this.inputEl().dom.disabled = false;
10732 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10734 // this.el.removeClass('x-item-disabled');
10739 onShow : function(){
10740 var ae = this.getActionEl();
10743 ae.dom.style.display = '';
10744 ae.dom.style.visibility = 'visible';
10750 onHide : function(){
10751 var ae = this.getActionEl();
10752 ae.dom.style.display = 'none';
10756 * The function that should handle the trigger's click event. This method does nothing by default until overridden
10757 * by an implementing function.
10759 * @param {EventObject} e
10761 onTriggerClick : Roo.emptyFn
10765 * Ext JS Library 1.1.1
10766 * Copyright(c) 2006-2007, Ext JS, LLC.
10768 * Originally Released Under LGPL - original licence link has changed is not relivant.
10771 * <script type="text/javascript">
10776 * @class Roo.data.SortTypes
10778 * Defines the default sorting (casting?) comparison functions used when sorting data.
10780 Roo.data.SortTypes = {
10782 * Default sort that does nothing
10783 * @param {Mixed} s The value being converted
10784 * @return {Mixed} The comparison value
10786 none : function(s){
10791 * The regular expression used to strip tags
10795 stripTagsRE : /<\/?[^>]+>/gi,
10798 * Strips all HTML tags to sort on text only
10799 * @param {Mixed} s The value being converted
10800 * @return {String} The comparison value
10802 asText : function(s){
10803 return String(s).replace(this.stripTagsRE, "");
10807 * Strips all HTML tags to sort on text only - Case insensitive
10808 * @param {Mixed} s The value being converted
10809 * @return {String} The comparison value
10811 asUCText : function(s){
10812 return String(s).toUpperCase().replace(this.stripTagsRE, "");
10816 * Case insensitive string
10817 * @param {Mixed} s The value being converted
10818 * @return {String} The comparison value
10820 asUCString : function(s) {
10821 return String(s).toUpperCase();
10826 * @param {Mixed} s The value being converted
10827 * @return {Number} The comparison value
10829 asDate : function(s) {
10833 if(s instanceof Date){
10834 return s.getTime();
10836 return Date.parse(String(s));
10841 * @param {Mixed} s The value being converted
10842 * @return {Float} The comparison value
10844 asFloat : function(s) {
10845 var val = parseFloat(String(s).replace(/,/g, ""));
10854 * @param {Mixed} s The value being converted
10855 * @return {Number} The comparison value
10857 asInt : function(s) {
10858 var val = parseInt(String(s).replace(/,/g, ""));
10866 * Ext JS Library 1.1.1
10867 * Copyright(c) 2006-2007, Ext JS, LLC.
10869 * Originally Released Under LGPL - original licence link has changed is not relivant.
10872 * <script type="text/javascript">
10876 * @class Roo.data.Record
10877 * Instances of this class encapsulate both record <em>definition</em> information, and record
10878 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10879 * to access Records cached in an {@link Roo.data.Store} object.<br>
10881 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10882 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10885 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10887 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10888 * {@link #create}. The parameters are the same.
10889 * @param {Array} data An associative Array of data values keyed by the field name.
10890 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10891 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10892 * not specified an integer id is generated.
10894 Roo.data.Record = function(data, id){
10895 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10900 * Generate a constructor for a specific record layout.
10901 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10902 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10903 * Each field definition object may contain the following properties: <ul>
10904 * <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,
10905 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10906 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10907 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10908 * is being used, then this is a string containing the javascript expression to reference the data relative to
10909 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10910 * to the data item relative to the record element. If the mapping expression is the same as the field name,
10911 * this may be omitted.</p></li>
10912 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10913 * <ul><li>auto (Default, implies no conversion)</li>
10918 * <li>date</li></ul></p></li>
10919 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10920 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10921 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10922 * by the Reader into an object that will be stored in the Record. It is passed the
10923 * following parameters:<ul>
10924 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10926 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10928 * <br>usage:<br><pre><code>
10929 var TopicRecord = Roo.data.Record.create(
10930 {name: 'title', mapping: 'topic_title'},
10931 {name: 'author', mapping: 'username'},
10932 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10933 {name: 'lastPost', mapping: 'post_time', type: 'date'},
10934 {name: 'lastPoster', mapping: 'user2'},
10935 {name: 'excerpt', mapping: 'post_text'}
10938 var myNewRecord = new TopicRecord({
10939 title: 'Do my job please',
10942 lastPost: new Date(),
10943 lastPoster: 'Animal',
10944 excerpt: 'No way dude!'
10946 myStore.add(myNewRecord);
10951 Roo.data.Record.create = function(o){
10952 var f = function(){
10953 f.superclass.constructor.apply(this, arguments);
10955 Roo.extend(f, Roo.data.Record);
10956 var p = f.prototype;
10957 p.fields = new Roo.util.MixedCollection(false, function(field){
10960 for(var i = 0, len = o.length; i < len; i++){
10961 p.fields.add(new Roo.data.Field(o[i]));
10963 f.getField = function(name){
10964 return p.fields.get(name);
10969 Roo.data.Record.AUTO_ID = 1000;
10970 Roo.data.Record.EDIT = 'edit';
10971 Roo.data.Record.REJECT = 'reject';
10972 Roo.data.Record.COMMIT = 'commit';
10974 Roo.data.Record.prototype = {
10976 * Readonly flag - true if this record has been modified.
10985 join : function(store){
10986 this.store = store;
10990 * Set the named field to the specified value.
10991 * @param {String} name The name of the field to set.
10992 * @param {Object} value The value to set the field to.
10994 set : function(name, value){
10995 if(this.data[name] == value){
10999 if(!this.modified){
11000 this.modified = {};
11002 if(typeof this.modified[name] == 'undefined'){
11003 this.modified[name] = this.data[name];
11005 this.data[name] = value;
11006 if(!this.editing && this.store){
11007 this.store.afterEdit(this);
11012 * Get the value of the named field.
11013 * @param {String} name The name of the field to get the value of.
11014 * @return {Object} The value of the field.
11016 get : function(name){
11017 return this.data[name];
11021 beginEdit : function(){
11022 this.editing = true;
11023 this.modified = {};
11027 cancelEdit : function(){
11028 this.editing = false;
11029 delete this.modified;
11033 endEdit : function(){
11034 this.editing = false;
11035 if(this.dirty && this.store){
11036 this.store.afterEdit(this);
11041 * Usually called by the {@link Roo.data.Store} which owns the Record.
11042 * Rejects all changes made to the Record since either creation, or the last commit operation.
11043 * Modified fields are reverted to their original values.
11045 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11046 * of reject operations.
11048 reject : function(){
11049 var m = this.modified;
11051 if(typeof m[n] != "function"){
11052 this.data[n] = m[n];
11055 this.dirty = false;
11056 delete this.modified;
11057 this.editing = false;
11059 this.store.afterReject(this);
11064 * Usually called by the {@link Roo.data.Store} which owns the Record.
11065 * Commits all changes made to the Record since either creation, or the last commit operation.
11067 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11068 * of commit operations.
11070 commit : function(){
11071 this.dirty = false;
11072 delete this.modified;
11073 this.editing = false;
11075 this.store.afterCommit(this);
11080 hasError : function(){
11081 return this.error != null;
11085 clearError : function(){
11090 * Creates a copy of this record.
11091 * @param {String} id (optional) A new record id if you don't want to use this record's id
11094 copy : function(newId) {
11095 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11099 * Ext JS Library 1.1.1
11100 * Copyright(c) 2006-2007, Ext JS, LLC.
11102 * Originally Released Under LGPL - original licence link has changed is not relivant.
11105 * <script type="text/javascript">
11111 * @class Roo.data.Store
11112 * @extends Roo.util.Observable
11113 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11114 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11116 * 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
11117 * has no knowledge of the format of the data returned by the Proxy.<br>
11119 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11120 * instances from the data object. These records are cached and made available through accessor functions.
11122 * Creates a new Store.
11123 * @param {Object} config A config object containing the objects needed for the Store to access data,
11124 * and read the data into Records.
11126 Roo.data.Store = function(config){
11127 this.data = new Roo.util.MixedCollection(false);
11128 this.data.getKey = function(o){
11131 this.baseParams = {};
11133 this.paramNames = {
11138 "multisort" : "_multisort"
11141 if(config && config.data){
11142 this.inlineData = config.data;
11143 delete config.data;
11146 Roo.apply(this, config);
11148 if(this.reader){ // reader passed
11149 this.reader = Roo.factory(this.reader, Roo.data);
11150 this.reader.xmodule = this.xmodule || false;
11151 if(!this.recordType){
11152 this.recordType = this.reader.recordType;
11154 if(this.reader.onMetaChange){
11155 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11159 if(this.recordType){
11160 this.fields = this.recordType.prototype.fields;
11162 this.modified = [];
11166 * @event datachanged
11167 * Fires when the data cache has changed, and a widget which is using this Store
11168 * as a Record cache should refresh its view.
11169 * @param {Store} this
11171 datachanged : true,
11173 * @event metachange
11174 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11175 * @param {Store} this
11176 * @param {Object} meta The JSON metadata
11181 * Fires when Records have been added to the Store
11182 * @param {Store} this
11183 * @param {Roo.data.Record[]} records The array of Records added
11184 * @param {Number} index The index at which the record(s) were added
11189 * Fires when a Record has been removed from the Store
11190 * @param {Store} this
11191 * @param {Roo.data.Record} record The Record that was removed
11192 * @param {Number} index The index at which the record was removed
11197 * Fires when a Record has been updated
11198 * @param {Store} this
11199 * @param {Roo.data.Record} record The Record that was updated
11200 * @param {String} operation The update operation being performed. Value may be one of:
11202 Roo.data.Record.EDIT
11203 Roo.data.Record.REJECT
11204 Roo.data.Record.COMMIT
11210 * Fires when the data cache has been cleared.
11211 * @param {Store} this
11215 * @event beforeload
11216 * Fires before a request is made for a new data object. If the beforeload handler returns false
11217 * the load action will be canceled.
11218 * @param {Store} this
11219 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11223 * @event beforeloadadd
11224 * Fires after a new set of Records has been loaded.
11225 * @param {Store} this
11226 * @param {Roo.data.Record[]} records The Records that were loaded
11227 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11229 beforeloadadd : true,
11232 * Fires after a new set of Records has been loaded, before they are added to the store.
11233 * @param {Store} this
11234 * @param {Roo.data.Record[]} records The Records that were loaded
11235 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11236 * @params {Object} return from reader
11240 * @event loadexception
11241 * Fires if an exception occurs in the Proxy during loading.
11242 * Called with the signature of the Proxy's "loadexception" event.
11243 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11246 * @param {Object} return from JsonData.reader() - success, totalRecords, records
11247 * @param {Object} load options
11248 * @param {Object} jsonData from your request (normally this contains the Exception)
11250 loadexception : true
11254 this.proxy = Roo.factory(this.proxy, Roo.data);
11255 this.proxy.xmodule = this.xmodule || false;
11256 this.relayEvents(this.proxy, ["loadexception"]);
11258 this.sortToggle = {};
11259 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11261 Roo.data.Store.superclass.constructor.call(this);
11263 if(this.inlineData){
11264 this.loadData(this.inlineData);
11265 delete this.inlineData;
11269 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11271 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
11272 * without a remote query - used by combo/forms at present.
11276 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11279 * @cfg {Array} data Inline data to be loaded when the store is initialized.
11282 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11283 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11286 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11287 * on any HTTP request
11290 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11293 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11297 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11298 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11300 remoteSort : false,
11303 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11304 * loaded or when a record is removed. (defaults to false).
11306 pruneModifiedRecords : false,
11309 lastOptions : null,
11312 * Add Records to the Store and fires the add event.
11313 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11315 add : function(records){
11316 records = [].concat(records);
11317 for(var i = 0, len = records.length; i < len; i++){
11318 records[i].join(this);
11320 var index = this.data.length;
11321 this.data.addAll(records);
11322 this.fireEvent("add", this, records, index);
11326 * Remove a Record from the Store and fires the remove event.
11327 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11329 remove : function(record){
11330 var index = this.data.indexOf(record);
11331 this.data.removeAt(index);
11333 if(this.pruneModifiedRecords){
11334 this.modified.remove(record);
11336 this.fireEvent("remove", this, record, index);
11340 * Remove all Records from the Store and fires the clear event.
11342 removeAll : function(){
11344 if(this.pruneModifiedRecords){
11345 this.modified = [];
11347 this.fireEvent("clear", this);
11351 * Inserts Records to the Store at the given index and fires the add event.
11352 * @param {Number} index The start index at which to insert the passed Records.
11353 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11355 insert : function(index, records){
11356 records = [].concat(records);
11357 for(var i = 0, len = records.length; i < len; i++){
11358 this.data.insert(index, records[i]);
11359 records[i].join(this);
11361 this.fireEvent("add", this, records, index);
11365 * Get the index within the cache of the passed Record.
11366 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11367 * @return {Number} The index of the passed Record. Returns -1 if not found.
11369 indexOf : function(record){
11370 return this.data.indexOf(record);
11374 * Get the index within the cache of the Record with the passed id.
11375 * @param {String} id The id of the Record to find.
11376 * @return {Number} The index of the Record. Returns -1 if not found.
11378 indexOfId : function(id){
11379 return this.data.indexOfKey(id);
11383 * Get the Record with the specified id.
11384 * @param {String} id The id of the Record to find.
11385 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11387 getById : function(id){
11388 return this.data.key(id);
11392 * Get the Record at the specified index.
11393 * @param {Number} index The index of the Record to find.
11394 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11396 getAt : function(index){
11397 return this.data.itemAt(index);
11401 * Returns a range of Records between specified indices.
11402 * @param {Number} startIndex (optional) The starting index (defaults to 0)
11403 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11404 * @return {Roo.data.Record[]} An array of Records
11406 getRange : function(start, end){
11407 return this.data.getRange(start, end);
11411 storeOptions : function(o){
11412 o = Roo.apply({}, o);
11415 this.lastOptions = o;
11419 * Loads the Record cache from the configured Proxy using the configured Reader.
11421 * If using remote paging, then the first load call must specify the <em>start</em>
11422 * and <em>limit</em> properties in the options.params property to establish the initial
11423 * position within the dataset, and the number of Records to cache on each read from the Proxy.
11425 * <strong>It is important to note that for remote data sources, loading is asynchronous,
11426 * and this call will return before the new data has been loaded. Perform any post-processing
11427 * in a callback function, or in a "load" event handler.</strong>
11429 * @param {Object} options An object containing properties which control loading options:<ul>
11430 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11431 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11432 * passed the following arguments:<ul>
11433 * <li>r : Roo.data.Record[]</li>
11434 * <li>options: Options object from the load call</li>
11435 * <li>success: Boolean success indicator</li></ul></li>
11436 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11437 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11440 load : function(options){
11441 options = options || {};
11442 if(this.fireEvent("beforeload", this, options) !== false){
11443 this.storeOptions(options);
11444 var p = Roo.apply(options.params || {}, this.baseParams);
11445 // if meta was not loaded from remote source.. try requesting it.
11446 if (!this.reader.metaFromRemote) {
11447 p._requestMeta = 1;
11449 if(this.sortInfo && this.remoteSort){
11450 var pn = this.paramNames;
11451 p[pn["sort"]] = this.sortInfo.field;
11452 p[pn["dir"]] = this.sortInfo.direction;
11454 if (this.multiSort) {
11455 var pn = this.paramNames;
11456 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11459 this.proxy.load(p, this.reader, this.loadRecords, this, options);
11464 * Reloads the Record cache from the configured Proxy using the configured Reader and
11465 * the options from the last load operation performed.
11466 * @param {Object} options (optional) An object containing properties which may override the options
11467 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11468 * the most recently used options are reused).
11470 reload : function(options){
11471 this.load(Roo.applyIf(options||{}, this.lastOptions));
11475 // Called as a callback by the Reader during a load operation.
11476 loadRecords : function(o, options, success){
11477 if(!o || success === false){
11478 if(success !== false){
11479 this.fireEvent("load", this, [], options, o);
11481 if(options.callback){
11482 options.callback.call(options.scope || this, [], options, false);
11486 // if data returned failure - throw an exception.
11487 if (o.success === false) {
11488 // show a message if no listener is registered.
11489 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11490 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11492 // loadmask wil be hooked into this..
11493 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11496 var r = o.records, t = o.totalRecords || r.length;
11498 this.fireEvent("beforeloadadd", this, r, options, o);
11500 if(!options || options.add !== true){
11501 if(this.pruneModifiedRecords){
11502 this.modified = [];
11504 for(var i = 0, len = r.length; i < len; i++){
11508 this.data = this.snapshot;
11509 delete this.snapshot;
11512 this.data.addAll(r);
11513 this.totalLength = t;
11515 this.fireEvent("datachanged", this);
11517 this.totalLength = Math.max(t, this.data.length+r.length);
11521 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11523 var e = new Roo.data.Record({});
11525 e.set(this.parent.displayField, this.parent.emptyTitle);
11526 e.set(this.parent.valueField, '');
11531 this.fireEvent("load", this, r, options, o);
11532 if(options.callback){
11533 options.callback.call(options.scope || this, r, options, true);
11539 * Loads data from a passed data block. A Reader which understands the format of the data
11540 * must have been configured in the constructor.
11541 * @param {Object} data The data block from which to read the Records. The format of the data expected
11542 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11543 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11545 loadData : function(o, append){
11546 var r = this.reader.readRecords(o);
11547 this.loadRecords(r, {add: append}, true);
11551 * Gets the number of cached records.
11553 * <em>If using paging, this may not be the total size of the dataset. If the data object
11554 * used by the Reader contains the dataset size, then the getTotalCount() function returns
11555 * the data set size</em>
11557 getCount : function(){
11558 return this.data.length || 0;
11562 * Gets the total number of records in the dataset as returned by the server.
11564 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11565 * the dataset size</em>
11567 getTotalCount : function(){
11568 return this.totalLength || 0;
11572 * Returns the sort state of the Store as an object with two properties:
11574 field {String} The name of the field by which the Records are sorted
11575 direction {String} The sort order, "ASC" or "DESC"
11578 getSortState : function(){
11579 return this.sortInfo;
11583 applySort : function(){
11584 if(this.sortInfo && !this.remoteSort){
11585 var s = this.sortInfo, f = s.field;
11586 var st = this.fields.get(f).sortType;
11587 var fn = function(r1, r2){
11588 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11589 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11591 this.data.sort(s.direction, fn);
11592 if(this.snapshot && this.snapshot != this.data){
11593 this.snapshot.sort(s.direction, fn);
11599 * Sets the default sort column and order to be used by the next load operation.
11600 * @param {String} fieldName The name of the field to sort by.
11601 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11603 setDefaultSort : function(field, dir){
11604 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11608 * Sort the Records.
11609 * If remote sorting is used, the sort is performed on the server, and the cache is
11610 * reloaded. If local sorting is used, the cache is sorted internally.
11611 * @param {String} fieldName The name of the field to sort by.
11612 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11614 sort : function(fieldName, dir){
11615 var f = this.fields.get(fieldName);
11617 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11619 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11620 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11625 this.sortToggle[f.name] = dir;
11626 this.sortInfo = {field: f.name, direction: dir};
11627 if(!this.remoteSort){
11629 this.fireEvent("datachanged", this);
11631 this.load(this.lastOptions);
11636 * Calls the specified function for each of the Records in the cache.
11637 * @param {Function} fn The function to call. The Record is passed as the first parameter.
11638 * Returning <em>false</em> aborts and exits the iteration.
11639 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11641 each : function(fn, scope){
11642 this.data.each(fn, scope);
11646 * Gets all records modified since the last commit. Modified records are persisted across load operations
11647 * (e.g., during paging).
11648 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11650 getModifiedRecords : function(){
11651 return this.modified;
11655 createFilterFn : function(property, value, anyMatch){
11656 if(!value.exec){ // not a regex
11657 value = String(value);
11658 if(value.length == 0){
11661 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11663 return function(r){
11664 return value.test(r.data[property]);
11669 * Sums the value of <i>property</i> for each record between start and end and returns the result.
11670 * @param {String} property A field on your records
11671 * @param {Number} start The record index to start at (defaults to 0)
11672 * @param {Number} end The last record index to include (defaults to length - 1)
11673 * @return {Number} The sum
11675 sum : function(property, start, end){
11676 var rs = this.data.items, v = 0;
11677 start = start || 0;
11678 end = (end || end === 0) ? end : rs.length-1;
11680 for(var i = start; i <= end; i++){
11681 v += (rs[i].data[property] || 0);
11687 * Filter the records by a specified property.
11688 * @param {String} field A field on your records
11689 * @param {String/RegExp} value Either a string that the field
11690 * should start with or a RegExp to test against the field
11691 * @param {Boolean} anyMatch True to match any part not just the beginning
11693 filter : function(property, value, anyMatch){
11694 var fn = this.createFilterFn(property, value, anyMatch);
11695 return fn ? this.filterBy(fn) : this.clearFilter();
11699 * Filter by a function. The specified function will be called with each
11700 * record in this data source. If the function returns true the record is included,
11701 * otherwise it is filtered.
11702 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11703 * @param {Object} scope (optional) The scope of the function (defaults to this)
11705 filterBy : function(fn, scope){
11706 this.snapshot = this.snapshot || this.data;
11707 this.data = this.queryBy(fn, scope||this);
11708 this.fireEvent("datachanged", this);
11712 * Query the records by a specified property.
11713 * @param {String} field A field on your records
11714 * @param {String/RegExp} value Either a string that the field
11715 * should start with or a RegExp to test against the field
11716 * @param {Boolean} anyMatch True to match any part not just the beginning
11717 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11719 query : function(property, value, anyMatch){
11720 var fn = this.createFilterFn(property, value, anyMatch);
11721 return fn ? this.queryBy(fn) : this.data.clone();
11725 * Query by a function. The specified function will be called with each
11726 * record in this data source. If the function returns true the record is included
11728 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11729 * @param {Object} scope (optional) The scope of the function (defaults to this)
11730 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11732 queryBy : function(fn, scope){
11733 var data = this.snapshot || this.data;
11734 return data.filterBy(fn, scope||this);
11738 * Collects unique values for a particular dataIndex from this store.
11739 * @param {String} dataIndex The property to collect
11740 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11741 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11742 * @return {Array} An array of the unique values
11744 collect : function(dataIndex, allowNull, bypassFilter){
11745 var d = (bypassFilter === true && this.snapshot) ?
11746 this.snapshot.items : this.data.items;
11747 var v, sv, r = [], l = {};
11748 for(var i = 0, len = d.length; i < len; i++){
11749 v = d[i].data[dataIndex];
11751 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11760 * Revert to a view of the Record cache with no filtering applied.
11761 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11763 clearFilter : function(suppressEvent){
11764 if(this.snapshot && this.snapshot != this.data){
11765 this.data = this.snapshot;
11766 delete this.snapshot;
11767 if(suppressEvent !== true){
11768 this.fireEvent("datachanged", this);
11774 afterEdit : function(record){
11775 if(this.modified.indexOf(record) == -1){
11776 this.modified.push(record);
11778 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11782 afterReject : function(record){
11783 this.modified.remove(record);
11784 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11788 afterCommit : function(record){
11789 this.modified.remove(record);
11790 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11794 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11795 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11797 commitChanges : function(){
11798 var m = this.modified.slice(0);
11799 this.modified = [];
11800 for(var i = 0, len = m.length; i < len; i++){
11806 * Cancel outstanding changes on all changed records.
11808 rejectChanges : function(){
11809 var m = this.modified.slice(0);
11810 this.modified = [];
11811 for(var i = 0, len = m.length; i < len; i++){
11816 onMetaChange : function(meta, rtype, o){
11817 this.recordType = rtype;
11818 this.fields = rtype.prototype.fields;
11819 delete this.snapshot;
11820 this.sortInfo = meta.sortInfo || this.sortInfo;
11821 this.modified = [];
11822 this.fireEvent('metachange', this, this.reader.meta);
11825 moveIndex : function(data, type)
11827 var index = this.indexOf(data);
11829 var newIndex = index + type;
11833 this.insert(newIndex, data);
11838 * Ext JS Library 1.1.1
11839 * Copyright(c) 2006-2007, Ext JS, LLC.
11841 * Originally Released Under LGPL - original licence link has changed is not relivant.
11844 * <script type="text/javascript">
11848 * @class Roo.data.SimpleStore
11849 * @extends Roo.data.Store
11850 * Small helper class to make creating Stores from Array data easier.
11851 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11852 * @cfg {Array} fields An array of field definition objects, or field name strings.
11853 * @cfg {Array} data The multi-dimensional array of data
11855 * @param {Object} config
11857 Roo.data.SimpleStore = function(config){
11858 Roo.data.SimpleStore.superclass.constructor.call(this, {
11860 reader: new Roo.data.ArrayReader({
11863 Roo.data.Record.create(config.fields)
11865 proxy : new Roo.data.MemoryProxy(config.data)
11869 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11871 * Ext JS Library 1.1.1
11872 * Copyright(c) 2006-2007, Ext JS, LLC.
11874 * Originally Released Under LGPL - original licence link has changed is not relivant.
11877 * <script type="text/javascript">
11882 * @extends Roo.data.Store
11883 * @class Roo.data.JsonStore
11884 * Small helper class to make creating Stores for JSON data easier. <br/>
11886 var store = new Roo.data.JsonStore({
11887 url: 'get-images.php',
11889 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11892 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11893 * JsonReader and HttpProxy (unless inline data is provided).</b>
11894 * @cfg {Array} fields An array of field definition objects, or field name strings.
11896 * @param {Object} config
11898 Roo.data.JsonStore = function(c){
11899 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11900 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11901 reader: new Roo.data.JsonReader(c, c.fields)
11904 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11906 * Ext JS Library 1.1.1
11907 * Copyright(c) 2006-2007, Ext JS, LLC.
11909 * Originally Released Under LGPL - original licence link has changed is not relivant.
11912 * <script type="text/javascript">
11916 Roo.data.Field = function(config){
11917 if(typeof config == "string"){
11918 config = {name: config};
11920 Roo.apply(this, config);
11923 this.type = "auto";
11926 var st = Roo.data.SortTypes;
11927 // named sortTypes are supported, here we look them up
11928 if(typeof this.sortType == "string"){
11929 this.sortType = st[this.sortType];
11932 // set default sortType for strings and dates
11933 if(!this.sortType){
11936 this.sortType = st.asUCString;
11939 this.sortType = st.asDate;
11942 this.sortType = st.none;
11947 var stripRe = /[\$,%]/g;
11949 // prebuilt conversion function for this field, instead of
11950 // switching every time we're reading a value
11952 var cv, dateFormat = this.dateFormat;
11957 cv = function(v){ return v; };
11960 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11964 return v !== undefined && v !== null && v !== '' ?
11965 parseInt(String(v).replace(stripRe, ""), 10) : '';
11970 return v !== undefined && v !== null && v !== '' ?
11971 parseFloat(String(v).replace(stripRe, ""), 10) : '';
11976 cv = function(v){ return v === true || v === "true" || v == 1; };
11983 if(v instanceof Date){
11987 if(dateFormat == "timestamp"){
11988 return new Date(v*1000);
11990 return Date.parseDate(v, dateFormat);
11992 var parsed = Date.parse(v);
11993 return parsed ? new Date(parsed) : null;
12002 Roo.data.Field.prototype = {
12010 * Ext JS Library 1.1.1
12011 * Copyright(c) 2006-2007, Ext JS, LLC.
12013 * Originally Released Under LGPL - original licence link has changed is not relivant.
12016 * <script type="text/javascript">
12019 // Base class for reading structured data from a data source. This class is intended to be
12020 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12023 * @class Roo.data.DataReader
12024 * Base class for reading structured data from a data source. This class is intended to be
12025 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12028 Roo.data.DataReader = function(meta, recordType){
12032 this.recordType = recordType instanceof Array ?
12033 Roo.data.Record.create(recordType) : recordType;
12036 Roo.data.DataReader.prototype = {
12038 * Create an empty record
12039 * @param {Object} data (optional) - overlay some values
12040 * @return {Roo.data.Record} record created.
12042 newRow : function(d) {
12044 this.recordType.prototype.fields.each(function(c) {
12046 case 'int' : da[c.name] = 0; break;
12047 case 'date' : da[c.name] = new Date(); break;
12048 case 'float' : da[c.name] = 0.0; break;
12049 case 'boolean' : da[c.name] = false; break;
12050 default : da[c.name] = ""; break;
12054 return new this.recordType(Roo.apply(da, d));
12059 * Ext JS Library 1.1.1
12060 * Copyright(c) 2006-2007, Ext JS, LLC.
12062 * Originally Released Under LGPL - original licence link has changed is not relivant.
12065 * <script type="text/javascript">
12069 * @class Roo.data.DataProxy
12070 * @extends Roo.data.Observable
12071 * This class is an abstract base class for implementations which provide retrieval of
12072 * unformatted data objects.<br>
12074 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12075 * (of the appropriate type which knows how to parse the data object) to provide a block of
12076 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12078 * Custom implementations must implement the load method as described in
12079 * {@link Roo.data.HttpProxy#load}.
12081 Roo.data.DataProxy = function(){
12084 * @event beforeload
12085 * Fires before a network request is made to retrieve a data object.
12086 * @param {Object} This DataProxy object.
12087 * @param {Object} params The params parameter to the load function.
12092 * Fires before the load method's callback is called.
12093 * @param {Object} This DataProxy object.
12094 * @param {Object} o The data object.
12095 * @param {Object} arg The callback argument object passed to the load function.
12099 * @event loadexception
12100 * Fires if an Exception occurs during data retrieval.
12101 * @param {Object} This DataProxy object.
12102 * @param {Object} o The data object.
12103 * @param {Object} arg The callback argument object passed to the load function.
12104 * @param {Object} e The Exception.
12106 loadexception : true
12108 Roo.data.DataProxy.superclass.constructor.call(this);
12111 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12114 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12118 * Ext JS Library 1.1.1
12119 * Copyright(c) 2006-2007, Ext JS, LLC.
12121 * Originally Released Under LGPL - original licence link has changed is not relivant.
12124 * <script type="text/javascript">
12127 * @class Roo.data.MemoryProxy
12128 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12129 * to the Reader when its load method is called.
12131 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12133 Roo.data.MemoryProxy = function(data){
12137 Roo.data.MemoryProxy.superclass.constructor.call(this);
12141 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12144 * Load data from the requested source (in this case an in-memory
12145 * data object passed to the constructor), read the data object into
12146 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12147 * process that block using the passed callback.
12148 * @param {Object} params This parameter is not used by the MemoryProxy class.
12149 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12150 * object into a block of Roo.data.Records.
12151 * @param {Function} callback The function into which to pass the block of Roo.data.records.
12152 * The function must be passed <ul>
12153 * <li>The Record block object</li>
12154 * <li>The "arg" argument from the load function</li>
12155 * <li>A boolean success indicator</li>
12157 * @param {Object} scope The scope in which to call the callback
12158 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12160 load : function(params, reader, callback, scope, arg){
12161 params = params || {};
12164 result = reader.readRecords(this.data);
12166 this.fireEvent("loadexception", this, arg, null, e);
12167 callback.call(scope, null, arg, false);
12170 callback.call(scope, result, arg, true);
12174 update : function(params, records){
12179 * Ext JS Library 1.1.1
12180 * Copyright(c) 2006-2007, Ext JS, LLC.
12182 * Originally Released Under LGPL - original licence link has changed is not relivant.
12185 * <script type="text/javascript">
12188 * @class Roo.data.HttpProxy
12189 * @extends Roo.data.DataProxy
12190 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12191 * configured to reference a certain URL.<br><br>
12193 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12194 * from which the running page was served.<br><br>
12196 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12198 * Be aware that to enable the browser to parse an XML document, the server must set
12199 * the Content-Type header in the HTTP response to "text/xml".
12201 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12202 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
12203 * will be used to make the request.
12205 Roo.data.HttpProxy = function(conn){
12206 Roo.data.HttpProxy.superclass.constructor.call(this);
12207 // is conn a conn config or a real conn?
12209 this.useAjax = !conn || !conn.events;
12213 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12214 // thse are take from connection...
12217 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12220 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12221 * extra parameters to each request made by this object. (defaults to undefined)
12224 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12225 * to each request made by this object. (defaults to undefined)
12228 * @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)
12231 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12234 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12240 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12244 * Return the {@link Roo.data.Connection} object being used by this Proxy.
12245 * @return {Connection} The Connection object. This object may be used to subscribe to events on
12246 * a finer-grained basis than the DataProxy events.
12248 getConnection : function(){
12249 return this.useAjax ? Roo.Ajax : this.conn;
12253 * Load data from the configured {@link Roo.data.Connection}, read the data object into
12254 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12255 * process that block using the passed callback.
12256 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12257 * for the request to the remote server.
12258 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12259 * object into a block of Roo.data.Records.
12260 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12261 * The function must be passed <ul>
12262 * <li>The Record block object</li>
12263 * <li>The "arg" argument from the load function</li>
12264 * <li>A boolean success indicator</li>
12266 * @param {Object} scope The scope in which to call the callback
12267 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12269 load : function(params, reader, callback, scope, arg){
12270 if(this.fireEvent("beforeload", this, params) !== false){
12272 params : params || {},
12274 callback : callback,
12279 callback : this.loadResponse,
12283 Roo.applyIf(o, this.conn);
12284 if(this.activeRequest){
12285 Roo.Ajax.abort(this.activeRequest);
12287 this.activeRequest = Roo.Ajax.request(o);
12289 this.conn.request(o);
12292 callback.call(scope||this, null, arg, false);
12297 loadResponse : function(o, success, response){
12298 delete this.activeRequest;
12300 this.fireEvent("loadexception", this, o, response);
12301 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12306 result = o.reader.read(response);
12308 this.fireEvent("loadexception", this, o, response, e);
12309 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12313 this.fireEvent("load", this, o, o.request.arg);
12314 o.request.callback.call(o.request.scope, result, o.request.arg, true);
12318 update : function(dataSet){
12323 updateResponse : function(dataSet){
12328 * Ext JS Library 1.1.1
12329 * Copyright(c) 2006-2007, Ext JS, LLC.
12331 * Originally Released Under LGPL - original licence link has changed is not relivant.
12334 * <script type="text/javascript">
12338 * @class Roo.data.ScriptTagProxy
12339 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12340 * other than the originating domain of the running page.<br><br>
12342 * <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
12343 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12345 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12346 * source code that is used as the source inside a <script> tag.<br><br>
12348 * In order for the browser to process the returned data, the server must wrap the data object
12349 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12350 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12351 * depending on whether the callback name was passed:
12354 boolean scriptTag = false;
12355 String cb = request.getParameter("callback");
12358 response.setContentType("text/javascript");
12360 response.setContentType("application/x-json");
12362 Writer out = response.getWriter();
12364 out.write(cb + "(");
12366 out.print(dataBlock.toJsonString());
12373 * @param {Object} config A configuration object.
12375 Roo.data.ScriptTagProxy = function(config){
12376 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12377 Roo.apply(this, config);
12378 this.head = document.getElementsByTagName("head")[0];
12381 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12383 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12385 * @cfg {String} url The URL from which to request the data object.
12388 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12392 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12393 * the server the name of the callback function set up by the load call to process the returned data object.
12394 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12395 * javascript output which calls this named function passing the data object as its only parameter.
12397 callbackParam : "callback",
12399 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12400 * name to the request.
12405 * Load data from the configured URL, read the data object into
12406 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12407 * process that block using the passed callback.
12408 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12409 * for the request to the remote server.
12410 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12411 * object into a block of Roo.data.Records.
12412 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12413 * The function must be passed <ul>
12414 * <li>The Record block object</li>
12415 * <li>The "arg" argument from the load function</li>
12416 * <li>A boolean success indicator</li>
12418 * @param {Object} scope The scope in which to call the callback
12419 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12421 load : function(params, reader, callback, scope, arg){
12422 if(this.fireEvent("beforeload", this, params) !== false){
12424 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12426 var url = this.url;
12427 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12429 url += "&_dc=" + (new Date().getTime());
12431 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12434 cb : "stcCallback"+transId,
12435 scriptId : "stcScript"+transId,
12439 callback : callback,
12445 window[trans.cb] = function(o){
12446 conn.handleResponse(o, trans);
12449 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12451 if(this.autoAbort !== false){
12455 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12457 var script = document.createElement("script");
12458 script.setAttribute("src", url);
12459 script.setAttribute("type", "text/javascript");
12460 script.setAttribute("id", trans.scriptId);
12461 this.head.appendChild(script);
12463 this.trans = trans;
12465 callback.call(scope||this, null, arg, false);
12470 isLoading : function(){
12471 return this.trans ? true : false;
12475 * Abort the current server request.
12477 abort : function(){
12478 if(this.isLoading()){
12479 this.destroyTrans(this.trans);
12484 destroyTrans : function(trans, isLoaded){
12485 this.head.removeChild(document.getElementById(trans.scriptId));
12486 clearTimeout(trans.timeoutId);
12488 window[trans.cb] = undefined;
12490 delete window[trans.cb];
12493 // if hasn't been loaded, wait for load to remove it to prevent script error
12494 window[trans.cb] = function(){
12495 window[trans.cb] = undefined;
12497 delete window[trans.cb];
12504 handleResponse : function(o, trans){
12505 this.trans = false;
12506 this.destroyTrans(trans, true);
12509 result = trans.reader.readRecords(o);
12511 this.fireEvent("loadexception", this, o, trans.arg, e);
12512 trans.callback.call(trans.scope||window, null, trans.arg, false);
12515 this.fireEvent("load", this, o, trans.arg);
12516 trans.callback.call(trans.scope||window, result, trans.arg, true);
12520 handleFailure : function(trans){
12521 this.trans = false;
12522 this.destroyTrans(trans, false);
12523 this.fireEvent("loadexception", this, null, trans.arg);
12524 trans.callback.call(trans.scope||window, null, trans.arg, false);
12528 * Ext JS Library 1.1.1
12529 * Copyright(c) 2006-2007, Ext JS, LLC.
12531 * Originally Released Under LGPL - original licence link has changed is not relivant.
12534 * <script type="text/javascript">
12538 * @class Roo.data.JsonReader
12539 * @extends Roo.data.DataReader
12540 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12541 * based on mappings in a provided Roo.data.Record constructor.
12543 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12544 * in the reply previously.
12549 var RecordDef = Roo.data.Record.create([
12550 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
12551 {name: 'occupation'} // This field will use "occupation" as the mapping.
12553 var myReader = new Roo.data.JsonReader({
12554 totalProperty: "results", // The property which contains the total dataset size (optional)
12555 root: "rows", // The property which contains an Array of row objects
12556 id: "id" // The property within each row object that provides an ID for the record (optional)
12560 * This would consume a JSON file like this:
12562 { 'results': 2, 'rows': [
12563 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12564 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12567 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12568 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12569 * paged from the remote server.
12570 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12571 * @cfg {String} root name of the property which contains the Array of row objects.
12572 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12573 * @cfg {Array} fields Array of field definition objects
12575 * Create a new JsonReader
12576 * @param {Object} meta Metadata configuration options
12577 * @param {Object} recordType Either an Array of field definition objects,
12578 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12580 Roo.data.JsonReader = function(meta, recordType){
12583 // set some defaults:
12584 Roo.applyIf(meta, {
12585 totalProperty: 'total',
12586 successProperty : 'success',
12591 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12593 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12596 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
12597 * Used by Store query builder to append _requestMeta to params.
12600 metaFromRemote : false,
12602 * This method is only used by a DataProxy which has retrieved data from a remote server.
12603 * @param {Object} response The XHR object which contains the JSON data in its responseText.
12604 * @return {Object} data A data block which is used by an Roo.data.Store object as
12605 * a cache of Roo.data.Records.
12607 read : function(response){
12608 var json = response.responseText;
12610 var o = /* eval:var:o */ eval("("+json+")");
12612 throw {message: "JsonReader.read: Json object not found"};
12618 this.metaFromRemote = true;
12619 this.meta = o.metaData;
12620 this.recordType = Roo.data.Record.create(o.metaData.fields);
12621 this.onMetaChange(this.meta, this.recordType, o);
12623 return this.readRecords(o);
12626 // private function a store will implement
12627 onMetaChange : function(meta, recordType, o){
12634 simpleAccess: function(obj, subsc) {
12641 getJsonAccessor: function(){
12643 return function(expr) {
12645 return(re.test(expr))
12646 ? new Function("obj", "return obj." + expr)
12651 return Roo.emptyFn;
12656 * Create a data block containing Roo.data.Records from an XML document.
12657 * @param {Object} o An object which contains an Array of row objects in the property specified
12658 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12659 * which contains the total size of the dataset.
12660 * @return {Object} data A data block which is used by an Roo.data.Store object as
12661 * a cache of Roo.data.Records.
12663 readRecords : function(o){
12665 * After any data loads, the raw JSON data is available for further custom processing.
12669 var s = this.meta, Record = this.recordType,
12670 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12672 // Generate extraction functions for the totalProperty, the root, the id, and for each field
12674 if(s.totalProperty) {
12675 this.getTotal = this.getJsonAccessor(s.totalProperty);
12677 if(s.successProperty) {
12678 this.getSuccess = this.getJsonAccessor(s.successProperty);
12680 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12682 var g = this.getJsonAccessor(s.id);
12683 this.getId = function(rec) {
12685 return (r === undefined || r === "") ? null : r;
12688 this.getId = function(){return null;};
12691 for(var jj = 0; jj < fl; jj++){
12693 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12694 this.ef[jj] = this.getJsonAccessor(map);
12698 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12699 if(s.totalProperty){
12700 var vt = parseInt(this.getTotal(o), 10);
12705 if(s.successProperty){
12706 var vs = this.getSuccess(o);
12707 if(vs === false || vs === 'false'){
12712 for(var i = 0; i < c; i++){
12715 var id = this.getId(n);
12716 for(var j = 0; j < fl; j++){
12718 var v = this.ef[j](n);
12720 Roo.log('missing convert for ' + f.name);
12724 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12726 var record = new Record(values, id);
12728 records[i] = record;
12734 totalRecords : totalRecords
12739 * Ext JS Library 1.1.1
12740 * Copyright(c) 2006-2007, Ext JS, LLC.
12742 * Originally Released Under LGPL - original licence link has changed is not relivant.
12745 * <script type="text/javascript">
12749 * @class Roo.data.ArrayReader
12750 * @extends Roo.data.DataReader
12751 * Data reader class to create an Array of Roo.data.Record objects from an Array.
12752 * Each element of that Array represents a row of data fields. The
12753 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12754 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12758 var RecordDef = Roo.data.Record.create([
12759 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
12760 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
12762 var myReader = new Roo.data.ArrayReader({
12763 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
12767 * This would consume an Array like this:
12769 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12771 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12773 * Create a new JsonReader
12774 * @param {Object} meta Metadata configuration options.
12775 * @param {Object} recordType Either an Array of field definition objects
12776 * as specified to {@link Roo.data.Record#create},
12777 * or an {@link Roo.data.Record} object
12778 * created using {@link Roo.data.Record#create}.
12780 Roo.data.ArrayReader = function(meta, recordType){
12781 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12784 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12786 * Create a data block containing Roo.data.Records from an XML document.
12787 * @param {Object} o An Array of row objects which represents the dataset.
12788 * @return {Object} data A data block which is used by an Roo.data.Store object as
12789 * a cache of Roo.data.Records.
12791 readRecords : function(o){
12792 var sid = this.meta ? this.meta.id : null;
12793 var recordType = this.recordType, fields = recordType.prototype.fields;
12796 for(var i = 0; i < root.length; i++){
12799 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12800 for(var j = 0, jlen = fields.length; j < jlen; j++){
12801 var f = fields.items[j];
12802 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12803 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12805 values[f.name] = v;
12807 var record = new recordType(values, id);
12809 records[records.length] = record;
12813 totalRecords : records.length
12822 * @class Roo.bootstrap.ComboBox
12823 * @extends Roo.bootstrap.TriggerField
12824 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12825 * @cfg {Boolean} append (true|false) default false
12826 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12827 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12828 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12829 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12830 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12831 * @cfg {Boolean} animate default true
12832 * @cfg {Boolean} emptyResultText only for touch device
12833 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12834 * @cfg {String} emptyTitle default ''
12836 * Create a new ComboBox.
12837 * @param {Object} config Configuration options
12839 Roo.bootstrap.ComboBox = function(config){
12840 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12844 * Fires when the dropdown list is expanded
12845 * @param {Roo.bootstrap.ComboBox} combo This combo box
12850 * Fires when the dropdown list is collapsed
12851 * @param {Roo.bootstrap.ComboBox} combo This combo box
12855 * @event beforeselect
12856 * Fires before a list item is selected. Return false to cancel the selection.
12857 * @param {Roo.bootstrap.ComboBox} combo This combo box
12858 * @param {Roo.data.Record} record The data record returned from the underlying store
12859 * @param {Number} index The index of the selected item in the dropdown list
12861 'beforeselect' : true,
12864 * Fires when a list item is selected
12865 * @param {Roo.bootstrap.ComboBox} combo This combo box
12866 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12867 * @param {Number} index The index of the selected item in the dropdown list
12871 * @event beforequery
12872 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12873 * The event object passed has these properties:
12874 * @param {Roo.bootstrap.ComboBox} combo This combo box
12875 * @param {String} query The query
12876 * @param {Boolean} forceAll true to force "all" query
12877 * @param {Boolean} cancel true to cancel the query
12878 * @param {Object} e The query event object
12880 'beforequery': true,
12883 * Fires when the 'add' icon is pressed (add a listener to enable add button)
12884 * @param {Roo.bootstrap.ComboBox} combo This combo box
12889 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12890 * @param {Roo.bootstrap.ComboBox} combo This combo box
12891 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12896 * Fires when the remove value from the combobox array
12897 * @param {Roo.bootstrap.ComboBox} combo This combo box
12901 * @event afterremove
12902 * Fires when the remove value from the combobox array
12903 * @param {Roo.bootstrap.ComboBox} combo This combo box
12905 'afterremove' : true,
12907 * @event specialfilter
12908 * Fires when specialfilter
12909 * @param {Roo.bootstrap.ComboBox} combo This combo box
12911 'specialfilter' : true,
12914 * Fires when tick the element
12915 * @param {Roo.bootstrap.ComboBox} combo This combo box
12919 * @event touchviewdisplay
12920 * Fires when touch view require special display (default is using displayField)
12921 * @param {Roo.bootstrap.ComboBox} combo This combo box
12922 * @param {Object} cfg set html .
12924 'touchviewdisplay' : true
12929 this.tickItems = [];
12931 this.selectedIndex = -1;
12932 if(this.mode == 'local'){
12933 if(config.queryDelay === undefined){
12934 this.queryDelay = 10;
12936 if(config.minChars === undefined){
12942 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12945 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12946 * rendering into an Roo.Editor, defaults to false)
12949 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12950 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12953 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12956 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12957 * the dropdown list (defaults to undefined, with no header element)
12961 * @cfg {String/Roo.Template} tpl The template to use to render the output
12965 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12967 listWidth: undefined,
12969 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12970 * mode = 'remote' or 'text' if mode = 'local')
12972 displayField: undefined,
12975 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12976 * mode = 'remote' or 'value' if mode = 'local').
12977 * Note: use of a valueField requires the user make a selection
12978 * in order for a value to be mapped.
12980 valueField: undefined,
12982 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12987 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12988 * field's data value (defaults to the underlying DOM element's name)
12990 hiddenName: undefined,
12992 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12996 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12998 selectedClass: 'active',
13001 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13005 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13006 * anchor positions (defaults to 'tl-bl')
13008 listAlign: 'tl-bl?',
13010 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13014 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
13015 * query specified by the allQuery config option (defaults to 'query')
13017 triggerAction: 'query',
13019 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13020 * (defaults to 4, does not apply if editable = false)
13024 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13025 * delay (typeAheadDelay) if it matches a known value (defaults to false)
13029 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13030 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13034 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13035 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
13039 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
13040 * when editable = true (defaults to false)
13042 selectOnFocus:false,
13044 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13046 queryParam: 'query',
13048 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
13049 * when mode = 'remote' (defaults to 'Loading...')
13051 loadingText: 'Loading...',
13053 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13057 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13061 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13062 * traditional select (defaults to true)
13066 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13070 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13074 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13075 * listWidth has a higher value)
13079 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13080 * allow the user to set arbitrary text into the field (defaults to false)
13082 forceSelection:false,
13084 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13085 * if typeAhead = true (defaults to 250)
13087 typeAheadDelay : 250,
13089 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13090 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13092 valueNotFoundText : undefined,
13094 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13096 blockFocus : false,
13099 * @cfg {Boolean} disableClear Disable showing of clear button.
13101 disableClear : false,
13103 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
13105 alwaysQuery : false,
13108 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
13113 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13115 invalidClass : "has-warning",
13118 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13120 validClass : "has-success",
13123 * @cfg {Boolean} specialFilter (true|false) special filter default false
13125 specialFilter : false,
13128 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13130 mobileTouchView : true,
13133 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13135 useNativeIOS : false,
13138 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13140 mobile_restrict_height : false,
13142 ios_options : false,
13154 btnPosition : 'right',
13155 triggerList : true,
13156 showToggleBtn : true,
13158 emptyResultText: 'Empty',
13159 triggerText : 'Select',
13162 // element that contains real text value.. (when hidden is used..)
13164 getAutoCreate : function()
13169 * Render classic select for iso
13172 if(Roo.isIOS && this.useNativeIOS){
13173 cfg = this.getAutoCreateNativeIOS();
13181 if(Roo.isTouch && this.mobileTouchView){
13182 cfg = this.getAutoCreateTouchView();
13189 if(!this.tickable){
13190 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13195 * ComboBox with tickable selections
13198 var align = this.labelAlign || this.parentLabelAlign();
13201 cls : 'form-group roo-combobox-tickable' //input-group
13204 var btn_text_select = '';
13205 var btn_text_done = '';
13206 var btn_text_cancel = '';
13208 if (this.btn_text_show) {
13209 btn_text_select = 'Select';
13210 btn_text_done = 'Done';
13211 btn_text_cancel = 'Cancel';
13216 cls : 'tickable-buttons',
13221 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13222 //html : this.triggerText
13223 html: btn_text_select
13229 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13231 html: btn_text_done
13237 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13239 html: btn_text_cancel
13245 buttons.cn.unshift({
13247 cls: 'roo-select2-search-field-input'
13253 Roo.each(buttons.cn, function(c){
13255 c.cls += ' btn-' + _this.size;
13258 if (_this.disabled) {
13269 cls: 'form-hidden-field'
13273 cls: 'roo-select2-choices',
13277 cls: 'roo-select2-search-field',
13288 cls: 'roo-select2-container input-group roo-select2-container-multi',
13293 // cls: 'typeahead typeahead-long dropdown-menu',
13294 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
13299 if(this.hasFeedback && !this.allowBlank){
13303 cls: 'glyphicon form-control-feedback'
13306 combobox.cn.push(feedback);
13310 if (align ==='left' && this.fieldLabel.length) {
13312 cfg.cls += ' roo-form-group-label-left';
13317 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13318 tooltip : 'This field is required'
13323 cls : 'control-label',
13324 html : this.fieldLabel
13336 var labelCfg = cfg.cn[1];
13337 var contentCfg = cfg.cn[2];
13340 if(this.indicatorpos == 'right'){
13346 cls : 'control-label',
13350 html : this.fieldLabel
13354 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13355 tooltip : 'This field is required'
13370 labelCfg = cfg.cn[0];
13371 contentCfg = cfg.cn[1];
13375 if(this.labelWidth > 12){
13376 labelCfg.style = "width: " + this.labelWidth + 'px';
13379 if(this.labelWidth < 13 && this.labelmd == 0){
13380 this.labelmd = this.labelWidth;
13383 if(this.labellg > 0){
13384 labelCfg.cls += ' col-lg-' + this.labellg;
13385 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13388 if(this.labelmd > 0){
13389 labelCfg.cls += ' col-md-' + this.labelmd;
13390 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13393 if(this.labelsm > 0){
13394 labelCfg.cls += ' col-sm-' + this.labelsm;
13395 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13398 if(this.labelxs > 0){
13399 labelCfg.cls += ' col-xs-' + this.labelxs;
13400 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13404 } else if ( this.fieldLabel.length) {
13405 // Roo.log(" label");
13409 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13410 tooltip : 'This field is required'
13414 //cls : 'input-group-addon',
13415 html : this.fieldLabel
13420 if(this.indicatorpos == 'right'){
13424 //cls : 'input-group-addon',
13425 html : this.fieldLabel
13429 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13430 tooltip : 'This field is required'
13439 // Roo.log(" no label && no align");
13446 ['xs','sm','md','lg'].map(function(size){
13447 if (settings[size]) {
13448 cfg.cls += ' col-' + size + '-' + settings[size];
13456 _initEventsCalled : false,
13459 initEvents: function()
13461 if (this._initEventsCalled) { // as we call render... prevent looping...
13464 this._initEventsCalled = true;
13467 throw "can not find store for combo";
13470 this.indicator = this.indicatorEl();
13472 this.store = Roo.factory(this.store, Roo.data);
13473 this.store.parent = this;
13475 // if we are building from html. then this element is so complex, that we can not really
13476 // use the rendered HTML.
13477 // so we have to trash and replace the previous code.
13478 if (Roo.XComponent.build_from_html) {
13479 // remove this element....
13480 var e = this.el.dom, k=0;
13481 while (e ) { e = e.previousSibling; ++k;}
13486 this.rendered = false;
13488 this.render(this.parent().getChildContainer(true), k);
13491 if(Roo.isIOS && this.useNativeIOS){
13492 this.initIOSView();
13500 if(Roo.isTouch && this.mobileTouchView){
13501 this.initTouchView();
13506 this.initTickableEvents();
13510 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13512 if(this.hiddenName){
13514 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13516 this.hiddenField.dom.value =
13517 this.hiddenValue !== undefined ? this.hiddenValue :
13518 this.value !== undefined ? this.value : '';
13520 // prevent input submission
13521 this.el.dom.removeAttribute('name');
13522 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13527 // this.el.dom.setAttribute('autocomplete', 'off');
13530 var cls = 'x-combo-list';
13532 //this.list = new Roo.Layer({
13533 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13539 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13540 _this.list.setWidth(lw);
13543 this.list.on('mouseover', this.onViewOver, this);
13544 this.list.on('mousemove', this.onViewMove, this);
13545 this.list.on('scroll', this.onViewScroll, this);
13548 this.list.swallowEvent('mousewheel');
13549 this.assetHeight = 0;
13552 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13553 this.assetHeight += this.header.getHeight();
13556 this.innerList = this.list.createChild({cls:cls+'-inner'});
13557 this.innerList.on('mouseover', this.onViewOver, this);
13558 this.innerList.on('mousemove', this.onViewMove, this);
13559 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13561 if(this.allowBlank && !this.pageSize && !this.disableClear){
13562 this.footer = this.list.createChild({cls:cls+'-ft'});
13563 this.pageTb = new Roo.Toolbar(this.footer);
13567 this.footer = this.list.createChild({cls:cls+'-ft'});
13568 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13569 {pageSize: this.pageSize});
13573 if (this.pageTb && this.allowBlank && !this.disableClear) {
13575 this.pageTb.add(new Roo.Toolbar.Fill(), {
13576 cls: 'x-btn-icon x-btn-clear',
13578 handler: function()
13581 _this.clearValue();
13582 _this.onSelect(false, -1);
13587 this.assetHeight += this.footer.getHeight();
13592 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13595 this.view = new Roo.View(this.list, this.tpl, {
13596 singleSelect:true, store: this.store, selectedClass: this.selectedClass
13598 //this.view.wrapEl.setDisplayed(false);
13599 this.view.on('click', this.onViewClick, this);
13602 this.store.on('beforeload', this.onBeforeLoad, this);
13603 this.store.on('load', this.onLoad, this);
13604 this.store.on('loadexception', this.onLoadException, this);
13606 if(this.resizable){
13607 this.resizer = new Roo.Resizable(this.list, {
13608 pinned:true, handles:'se'
13610 this.resizer.on('resize', function(r, w, h){
13611 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13612 this.listWidth = w;
13613 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13614 this.restrictHeight();
13616 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13619 if(!this.editable){
13620 this.editable = true;
13621 this.setEditable(false);
13626 if (typeof(this.events.add.listeners) != 'undefined') {
13628 this.addicon = this.wrap.createChild(
13629 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
13631 this.addicon.on('click', function(e) {
13632 this.fireEvent('add', this);
13635 if (typeof(this.events.edit.listeners) != 'undefined') {
13637 this.editicon = this.wrap.createChild(
13638 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
13639 if (this.addicon) {
13640 this.editicon.setStyle('margin-left', '40px');
13642 this.editicon.on('click', function(e) {
13644 // we fire even if inothing is selected..
13645 this.fireEvent('edit', this, this.lastData );
13651 this.keyNav = new Roo.KeyNav(this.inputEl(), {
13652 "up" : function(e){
13653 this.inKeyMode = true;
13657 "down" : function(e){
13658 if(!this.isExpanded()){
13659 this.onTriggerClick();
13661 this.inKeyMode = true;
13666 "enter" : function(e){
13667 // this.onViewClick();
13671 if(this.fireEvent("specialkey", this, e)){
13672 this.onViewClick(false);
13678 "esc" : function(e){
13682 "tab" : function(e){
13685 if(this.fireEvent("specialkey", this, e)){
13686 this.onViewClick(false);
13694 doRelay : function(foo, bar, hname){
13695 if(hname == 'down' || this.scope.isExpanded()){
13696 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13705 this.queryDelay = Math.max(this.queryDelay || 10,
13706 this.mode == 'local' ? 10 : 250);
13709 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13711 if(this.typeAhead){
13712 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13714 if(this.editable !== false){
13715 this.inputEl().on("keyup", this.onKeyUp, this);
13717 if(this.forceSelection){
13718 this.inputEl().on('blur', this.doForce, this);
13722 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13723 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13727 initTickableEvents: function()
13731 if(this.hiddenName){
13733 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13735 this.hiddenField.dom.value =
13736 this.hiddenValue !== undefined ? this.hiddenValue :
13737 this.value !== undefined ? this.value : '';
13739 // prevent input submission
13740 this.el.dom.removeAttribute('name');
13741 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13746 // this.list = this.el.select('ul.dropdown-menu',true).first();
13748 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13749 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13750 if(this.triggerList){
13751 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13754 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13755 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13757 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13758 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13760 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13761 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13763 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13764 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13765 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13768 this.cancelBtn.hide();
13773 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13774 _this.list.setWidth(lw);
13777 this.list.on('mouseover', this.onViewOver, this);
13778 this.list.on('mousemove', this.onViewMove, this);
13780 this.list.on('scroll', this.onViewScroll, this);
13783 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
13784 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13787 this.view = new Roo.View(this.list, this.tpl, {
13792 selectedClass: this.selectedClass
13795 //this.view.wrapEl.setDisplayed(false);
13796 this.view.on('click', this.onViewClick, this);
13800 this.store.on('beforeload', this.onBeforeLoad, this);
13801 this.store.on('load', this.onLoad, this);
13802 this.store.on('loadexception', this.onLoadException, this);
13805 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13806 "up" : function(e){
13807 this.inKeyMode = true;
13811 "down" : function(e){
13812 this.inKeyMode = true;
13816 "enter" : function(e){
13817 if(this.fireEvent("specialkey", this, e)){
13818 this.onViewClick(false);
13824 "esc" : function(e){
13825 this.onTickableFooterButtonClick(e, false, false);
13828 "tab" : function(e){
13829 this.fireEvent("specialkey", this, e);
13831 this.onTickableFooterButtonClick(e, false, false);
13838 doRelay : function(e, fn, key){
13839 if(this.scope.isExpanded()){
13840 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13849 this.queryDelay = Math.max(this.queryDelay || 10,
13850 this.mode == 'local' ? 10 : 250);
13853 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13855 if(this.typeAhead){
13856 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13859 if(this.editable !== false){
13860 this.tickableInputEl().on("keyup", this.onKeyUp, this);
13863 this.indicator = this.indicatorEl();
13865 if(this.indicator){
13866 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13867 this.indicator.hide();
13872 onDestroy : function(){
13874 this.view.setStore(null);
13875 this.view.el.removeAllListeners();
13876 this.view.el.remove();
13877 this.view.purgeListeners();
13880 this.list.dom.innerHTML = '';
13884 this.store.un('beforeload', this.onBeforeLoad, this);
13885 this.store.un('load', this.onLoad, this);
13886 this.store.un('loadexception', this.onLoadException, this);
13888 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13892 fireKey : function(e){
13893 if(e.isNavKeyPress() && !this.list.isVisible()){
13894 this.fireEvent("specialkey", this, e);
13899 onResize: function(w, h){
13900 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13902 // if(typeof w != 'number'){
13903 // // we do not handle it!?!?
13906 // var tw = this.trigger.getWidth();
13907 // // tw += this.addicon ? this.addicon.getWidth() : 0;
13908 // // tw += this.editicon ? this.editicon.getWidth() : 0;
13910 // this.inputEl().setWidth( this.adjustWidth('input', x));
13912 // //this.trigger.setStyle('left', x+'px');
13914 // if(this.list && this.listWidth === undefined){
13915 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13916 // this.list.setWidth(lw);
13917 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13925 * Allow or prevent the user from directly editing the field text. If false is passed,
13926 * the user will only be able to select from the items defined in the dropdown list. This method
13927 * is the runtime equivalent of setting the 'editable' config option at config time.
13928 * @param {Boolean} value True to allow the user to directly edit the field text
13930 setEditable : function(value){
13931 if(value == this.editable){
13934 this.editable = value;
13936 this.inputEl().dom.setAttribute('readOnly', true);
13937 this.inputEl().on('mousedown', this.onTriggerClick, this);
13938 this.inputEl().addClass('x-combo-noedit');
13940 this.inputEl().dom.setAttribute('readOnly', false);
13941 this.inputEl().un('mousedown', this.onTriggerClick, this);
13942 this.inputEl().removeClass('x-combo-noedit');
13948 onBeforeLoad : function(combo,opts){
13949 if(!this.hasFocus){
13953 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13955 this.restrictHeight();
13956 this.selectedIndex = -1;
13960 onLoad : function(){
13962 this.hasQuery = false;
13964 if(!this.hasFocus){
13968 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13969 this.loading.hide();
13972 if(this.store.getCount() > 0){
13975 this.restrictHeight();
13976 if(this.lastQuery == this.allQuery){
13977 if(this.editable && !this.tickable){
13978 this.inputEl().dom.select();
13982 !this.selectByValue(this.value, true) &&
13985 !this.store.lastOptions ||
13986 typeof(this.store.lastOptions.add) == 'undefined' ||
13987 this.store.lastOptions.add != true
13990 this.select(0, true);
13993 if(this.autoFocus){
13996 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13997 this.taTask.delay(this.typeAheadDelay);
14001 this.onEmptyResults();
14007 onLoadException : function()
14009 this.hasQuery = false;
14011 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14012 this.loading.hide();
14015 if(this.tickable && this.editable){
14020 // only causes errors at present
14021 //Roo.log(this.store.reader.jsonData);
14022 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14024 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14030 onTypeAhead : function(){
14031 if(this.store.getCount() > 0){
14032 var r = this.store.getAt(0);
14033 var newValue = r.data[this.displayField];
14034 var len = newValue.length;
14035 var selStart = this.getRawValue().length;
14037 if(selStart != len){
14038 this.setRawValue(newValue);
14039 this.selectText(selStart, newValue.length);
14045 onSelect : function(record, index){
14047 if(this.fireEvent('beforeselect', this, record, index) !== false){
14049 this.setFromData(index > -1 ? record.data : false);
14052 this.fireEvent('select', this, record, index);
14057 * Returns the currently selected field value or empty string if no value is set.
14058 * @return {String} value The selected value
14060 getValue : function()
14062 if(Roo.isIOS && this.useNativeIOS){
14063 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14067 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14070 if(this.valueField){
14071 return typeof this.value != 'undefined' ? this.value : '';
14073 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14077 getRawValue : function()
14079 if(Roo.isIOS && this.useNativeIOS){
14080 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14083 var v = this.inputEl().getValue();
14089 * Clears any text/value currently set in the field
14091 clearValue : function(){
14093 if(this.hiddenField){
14094 this.hiddenField.dom.value = '';
14097 this.setRawValue('');
14098 this.lastSelectionText = '';
14099 this.lastData = false;
14101 var close = this.closeTriggerEl();
14112 * Sets the specified value into the field. If the value finds a match, the corresponding record text
14113 * will be displayed in the field. If the value does not match the data value of an existing item,
14114 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14115 * Otherwise the field will be blank (although the value will still be set).
14116 * @param {String} value The value to match
14118 setValue : function(v)
14120 if(Roo.isIOS && this.useNativeIOS){
14121 this.setIOSValue(v);
14131 if(this.valueField){
14132 var r = this.findRecord(this.valueField, v);
14134 text = r.data[this.displayField];
14135 }else if(this.valueNotFoundText !== undefined){
14136 text = this.valueNotFoundText;
14139 this.lastSelectionText = text;
14140 if(this.hiddenField){
14141 this.hiddenField.dom.value = v;
14143 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14146 var close = this.closeTriggerEl();
14149 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14155 * @property {Object} the last set data for the element
14160 * Sets the value of the field based on a object which is related to the record format for the store.
14161 * @param {Object} value the value to set as. or false on reset?
14163 setFromData : function(o){
14170 var dv = ''; // display value
14171 var vv = ''; // value value..
14173 if (this.displayField) {
14174 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14176 // this is an error condition!!!
14177 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14180 if(this.valueField){
14181 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14184 var close = this.closeTriggerEl();
14187 if(dv.length || vv * 1 > 0){
14189 this.blockFocus=true;
14195 if(this.hiddenField){
14196 this.hiddenField.dom.value = vv;
14198 this.lastSelectionText = dv;
14199 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14203 // no hidden field.. - we store the value in 'value', but still display
14204 // display field!!!!
14205 this.lastSelectionText = dv;
14206 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14213 reset : function(){
14214 // overridden so that last data is reset..
14221 this.setValue(this.originalValue);
14222 //this.clearInvalid();
14223 this.lastData = false;
14225 this.view.clearSelections();
14231 findRecord : function(prop, value){
14233 if(this.store.getCount() > 0){
14234 this.store.each(function(r){
14235 if(r.data[prop] == value){
14245 getName: function()
14247 // returns hidden if it's set..
14248 if (!this.rendered) {return ''};
14249 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
14253 onViewMove : function(e, t){
14254 this.inKeyMode = false;
14258 onViewOver : function(e, t){
14259 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14262 var item = this.view.findItemFromChild(t);
14265 var index = this.view.indexOf(item);
14266 this.select(index, false);
14271 onViewClick : function(view, doFocus, el, e)
14273 var index = this.view.getSelectedIndexes()[0];
14275 var r = this.store.getAt(index);
14279 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14286 Roo.each(this.tickItems, function(v,k){
14288 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14290 _this.tickItems.splice(k, 1);
14292 if(typeof(e) == 'undefined' && view == false){
14293 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14305 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14306 this.tickItems.push(r.data);
14309 if(typeof(e) == 'undefined' && view == false){
14310 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14317 this.onSelect(r, index);
14319 if(doFocus !== false && !this.blockFocus){
14320 this.inputEl().focus();
14325 restrictHeight : function(){
14326 //this.innerList.dom.style.height = '';
14327 //var inner = this.innerList.dom;
14328 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14329 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14330 //this.list.beginUpdate();
14331 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14332 this.list.alignTo(this.inputEl(), this.listAlign);
14333 this.list.alignTo(this.inputEl(), this.listAlign);
14334 //this.list.endUpdate();
14338 onEmptyResults : function(){
14340 if(this.tickable && this.editable){
14341 this.hasFocus = false;
14342 this.restrictHeight();
14350 * Returns true if the dropdown list is expanded, else false.
14352 isExpanded : function(){
14353 return this.list.isVisible();
14357 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14358 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14359 * @param {String} value The data value of the item to select
14360 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14361 * selected item if it is not currently in view (defaults to true)
14362 * @return {Boolean} True if the value matched an item in the list, else false
14364 selectByValue : function(v, scrollIntoView){
14365 if(v !== undefined && v !== null){
14366 var r = this.findRecord(this.valueField || this.displayField, v);
14368 this.select(this.store.indexOf(r), scrollIntoView);
14376 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14377 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14378 * @param {Number} index The zero-based index of the list item to select
14379 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14380 * selected item if it is not currently in view (defaults to true)
14382 select : function(index, scrollIntoView){
14383 this.selectedIndex = index;
14384 this.view.select(index);
14385 if(scrollIntoView !== false){
14386 var el = this.view.getNode(index);
14388 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14391 this.list.scrollChildIntoView(el, false);
14397 selectNext : function(){
14398 var ct = this.store.getCount();
14400 if(this.selectedIndex == -1){
14402 }else if(this.selectedIndex < ct-1){
14403 this.select(this.selectedIndex+1);
14409 selectPrev : function(){
14410 var ct = this.store.getCount();
14412 if(this.selectedIndex == -1){
14414 }else if(this.selectedIndex != 0){
14415 this.select(this.selectedIndex-1);
14421 onKeyUp : function(e){
14422 if(this.editable !== false && !e.isSpecialKey()){
14423 this.lastKey = e.getKey();
14424 this.dqTask.delay(this.queryDelay);
14429 validateBlur : function(){
14430 return !this.list || !this.list.isVisible();
14434 initQuery : function(){
14436 var v = this.getRawValue();
14438 if(this.tickable && this.editable){
14439 v = this.tickableInputEl().getValue();
14446 doForce : function(){
14447 if(this.inputEl().dom.value.length > 0){
14448 this.inputEl().dom.value =
14449 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14455 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
14456 * query allowing the query action to be canceled if needed.
14457 * @param {String} query The SQL query to execute
14458 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14459 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
14460 * saved in the current store (defaults to false)
14462 doQuery : function(q, forceAll){
14464 if(q === undefined || q === null){
14469 forceAll: forceAll,
14473 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14478 forceAll = qe.forceAll;
14479 if(forceAll === true || (q.length >= this.minChars)){
14481 this.hasQuery = true;
14483 if(this.lastQuery != q || this.alwaysQuery){
14484 this.lastQuery = q;
14485 if(this.mode == 'local'){
14486 this.selectedIndex = -1;
14488 this.store.clearFilter();
14491 if(this.specialFilter){
14492 this.fireEvent('specialfilter', this);
14497 this.store.filter(this.displayField, q);
14500 this.store.fireEvent("datachanged", this.store);
14507 this.store.baseParams[this.queryParam] = q;
14509 var options = {params : this.getParams(q)};
14512 options.add = true;
14513 options.params.start = this.page * this.pageSize;
14516 this.store.load(options);
14519 * this code will make the page width larger, at the beginning, the list not align correctly,
14520 * we should expand the list on onLoad
14521 * so command out it
14526 this.selectedIndex = -1;
14531 this.loadNext = false;
14535 getParams : function(q){
14537 //p[this.queryParam] = q;
14541 p.limit = this.pageSize;
14547 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14549 collapse : function(){
14550 if(!this.isExpanded()){
14556 this.hasFocus = false;
14560 this.cancelBtn.hide();
14561 this.trigger.show();
14564 this.tickableInputEl().dom.value = '';
14565 this.tickableInputEl().blur();
14570 Roo.get(document).un('mousedown', this.collapseIf, this);
14571 Roo.get(document).un('mousewheel', this.collapseIf, this);
14572 if (!this.editable) {
14573 Roo.get(document).un('keydown', this.listKeyPress, this);
14575 this.fireEvent('collapse', this);
14581 collapseIf : function(e){
14582 var in_combo = e.within(this.el);
14583 var in_list = e.within(this.list);
14584 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14586 if (in_combo || in_list || is_list) {
14587 //e.stopPropagation();
14592 this.onTickableFooterButtonClick(e, false, false);
14600 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14602 expand : function(){
14604 if(this.isExpanded() || !this.hasFocus){
14608 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14609 this.list.setWidth(lw);
14615 this.restrictHeight();
14619 this.tickItems = Roo.apply([], this.item);
14622 this.cancelBtn.show();
14623 this.trigger.hide();
14626 this.tickableInputEl().focus();
14631 Roo.get(document).on('mousedown', this.collapseIf, this);
14632 Roo.get(document).on('mousewheel', this.collapseIf, this);
14633 if (!this.editable) {
14634 Roo.get(document).on('keydown', this.listKeyPress, this);
14637 this.fireEvent('expand', this);
14641 // Implements the default empty TriggerField.onTriggerClick function
14642 onTriggerClick : function(e)
14644 Roo.log('trigger click');
14646 if(this.disabled || !this.triggerList){
14651 this.loadNext = false;
14653 if(this.isExpanded()){
14655 if (!this.blockFocus) {
14656 this.inputEl().focus();
14660 this.hasFocus = true;
14661 if(this.triggerAction == 'all') {
14662 this.doQuery(this.allQuery, true);
14664 this.doQuery(this.getRawValue());
14666 if (!this.blockFocus) {
14667 this.inputEl().focus();
14672 onTickableTriggerClick : function(e)
14679 this.loadNext = false;
14680 this.hasFocus = true;
14682 if(this.triggerAction == 'all') {
14683 this.doQuery(this.allQuery, true);
14685 this.doQuery(this.getRawValue());
14689 onSearchFieldClick : function(e)
14691 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14692 this.onTickableFooterButtonClick(e, false, false);
14696 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14701 this.loadNext = false;
14702 this.hasFocus = true;
14704 if(this.triggerAction == 'all') {
14705 this.doQuery(this.allQuery, true);
14707 this.doQuery(this.getRawValue());
14711 listKeyPress : function(e)
14713 //Roo.log('listkeypress');
14714 // scroll to first matching element based on key pres..
14715 if (e.isSpecialKey()) {
14718 var k = String.fromCharCode(e.getKey()).toUpperCase();
14721 var csel = this.view.getSelectedNodes();
14722 var cselitem = false;
14724 var ix = this.view.indexOf(csel[0]);
14725 cselitem = this.store.getAt(ix);
14726 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14732 this.store.each(function(v) {
14734 // start at existing selection.
14735 if (cselitem.id == v.id) {
14741 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14742 match = this.store.indexOf(v);
14748 if (match === false) {
14749 return true; // no more action?
14752 this.view.select(match);
14753 var sn = Roo.get(this.view.getSelectedNodes()[0]);
14754 sn.scrollIntoView(sn.dom.parentNode, false);
14757 onViewScroll : function(e, t){
14759 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){
14763 this.hasQuery = true;
14765 this.loading = this.list.select('.loading', true).first();
14767 if(this.loading === null){
14768 this.list.createChild({
14770 cls: 'loading roo-select2-more-results roo-select2-active',
14771 html: 'Loading more results...'
14774 this.loading = this.list.select('.loading', true).first();
14776 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14778 this.loading.hide();
14781 this.loading.show();
14786 this.loadNext = true;
14788 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14793 addItem : function(o)
14795 var dv = ''; // display value
14797 if (this.displayField) {
14798 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14800 // this is an error condition!!!
14801 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14808 var choice = this.choices.createChild({
14810 cls: 'roo-select2-search-choice',
14819 cls: 'roo-select2-search-choice-close fa fa-times',
14824 }, this.searchField);
14826 var close = choice.select('a.roo-select2-search-choice-close', true).first();
14828 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14836 this.inputEl().dom.value = '';
14841 onRemoveItem : function(e, _self, o)
14843 e.preventDefault();
14845 this.lastItem = Roo.apply([], this.item);
14847 var index = this.item.indexOf(o.data) * 1;
14850 Roo.log('not this item?!');
14854 this.item.splice(index, 1);
14859 this.fireEvent('remove', this, e);
14865 syncValue : function()
14867 if(!this.item.length){
14874 Roo.each(this.item, function(i){
14875 if(_this.valueField){
14876 value.push(i[_this.valueField]);
14883 this.value = value.join(',');
14885 if(this.hiddenField){
14886 this.hiddenField.dom.value = this.value;
14889 this.store.fireEvent("datachanged", this.store);
14894 clearItem : function()
14896 if(!this.multiple){
14902 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14910 if(this.tickable && !Roo.isTouch){
14911 this.view.refresh();
14915 inputEl: function ()
14917 if(Roo.isIOS && this.useNativeIOS){
14918 return this.el.select('select.roo-ios-select', true).first();
14921 if(Roo.isTouch && this.mobileTouchView){
14922 return this.el.select('input.form-control',true).first();
14926 return this.searchField;
14929 return this.el.select('input.form-control',true).first();
14932 onTickableFooterButtonClick : function(e, btn, el)
14934 e.preventDefault();
14936 this.lastItem = Roo.apply([], this.item);
14938 if(btn && btn.name == 'cancel'){
14939 this.tickItems = Roo.apply([], this.item);
14948 Roo.each(this.tickItems, function(o){
14956 validate : function()
14958 if(this.getVisibilityEl().hasClass('hidden')){
14962 var v = this.getRawValue();
14965 v = this.getValue();
14968 if(this.disabled || this.allowBlank || v.length){
14973 this.markInvalid();
14977 tickableInputEl : function()
14979 if(!this.tickable || !this.editable){
14980 return this.inputEl();
14983 return this.inputEl().select('.roo-select2-search-field-input', true).first();
14987 getAutoCreateTouchView : function()
14992 cls: 'form-group' //input-group
14998 type : this.inputType,
14999 cls : 'form-control x-combo-noedit',
15000 autocomplete: 'new-password',
15001 placeholder : this.placeholder || '',
15006 input.name = this.name;
15010 input.cls += ' input-' + this.size;
15013 if (this.disabled) {
15014 input.disabled = true;
15025 inputblock.cls += ' input-group';
15027 inputblock.cn.unshift({
15029 cls : 'input-group-addon',
15034 if(this.removable && !this.multiple){
15035 inputblock.cls += ' roo-removable';
15037 inputblock.cn.push({
15040 cls : 'roo-combo-removable-btn close'
15044 if(this.hasFeedback && !this.allowBlank){
15046 inputblock.cls += ' has-feedback';
15048 inputblock.cn.push({
15050 cls: 'glyphicon form-control-feedback'
15057 inputblock.cls += (this.before) ? '' : ' input-group';
15059 inputblock.cn.push({
15061 cls : 'input-group-addon',
15072 cls: 'form-hidden-field'
15086 cls: 'form-hidden-field'
15090 cls: 'roo-select2-choices',
15094 cls: 'roo-select2-search-field',
15107 cls: 'roo-select2-container input-group roo-touchview-combobox ',
15113 if(!this.multiple && this.showToggleBtn){
15120 if (this.caret != false) {
15123 cls: 'fa fa-' + this.caret
15130 cls : 'input-group-addon btn dropdown-toggle',
15135 cls: 'combobox-clear',
15149 combobox.cls += ' roo-select2-container-multi';
15152 var align = this.labelAlign || this.parentLabelAlign();
15154 if (align ==='left' && this.fieldLabel.length) {
15159 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15160 tooltip : 'This field is required'
15164 cls : 'control-label',
15165 html : this.fieldLabel
15176 var labelCfg = cfg.cn[1];
15177 var contentCfg = cfg.cn[2];
15180 if(this.indicatorpos == 'right'){
15185 cls : 'control-label',
15189 html : this.fieldLabel
15193 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15194 tooltip : 'This field is required'
15207 labelCfg = cfg.cn[0];
15208 contentCfg = cfg.cn[1];
15213 if(this.labelWidth > 12){
15214 labelCfg.style = "width: " + this.labelWidth + 'px';
15217 if(this.labelWidth < 13 && this.labelmd == 0){
15218 this.labelmd = this.labelWidth;
15221 if(this.labellg > 0){
15222 labelCfg.cls += ' col-lg-' + this.labellg;
15223 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15226 if(this.labelmd > 0){
15227 labelCfg.cls += ' col-md-' + this.labelmd;
15228 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15231 if(this.labelsm > 0){
15232 labelCfg.cls += ' col-sm-' + this.labelsm;
15233 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15236 if(this.labelxs > 0){
15237 labelCfg.cls += ' col-xs-' + this.labelxs;
15238 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15242 } else if ( this.fieldLabel.length) {
15246 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15247 tooltip : 'This field is required'
15251 cls : 'control-label',
15252 html : this.fieldLabel
15263 if(this.indicatorpos == 'right'){
15267 cls : 'control-label',
15268 html : this.fieldLabel,
15272 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15273 tooltip : 'This field is required'
15290 var settings = this;
15292 ['xs','sm','md','lg'].map(function(size){
15293 if (settings[size]) {
15294 cfg.cls += ' col-' + size + '-' + settings[size];
15301 initTouchView : function()
15303 this.renderTouchView();
15305 this.touchViewEl.on('scroll', function(){
15306 this.el.dom.scrollTop = 0;
15309 this.originalValue = this.getValue();
15311 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15313 this.inputEl().on("click", this.showTouchView, this);
15314 if (this.triggerEl) {
15315 this.triggerEl.on("click", this.showTouchView, this);
15319 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15320 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15322 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15324 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15325 this.store.on('load', this.onTouchViewLoad, this);
15326 this.store.on('loadexception', this.onTouchViewLoadException, this);
15328 if(this.hiddenName){
15330 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15332 this.hiddenField.dom.value =
15333 this.hiddenValue !== undefined ? this.hiddenValue :
15334 this.value !== undefined ? this.value : '';
15336 this.el.dom.removeAttribute('name');
15337 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15341 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15342 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15345 if(this.removable && !this.multiple){
15346 var close = this.closeTriggerEl();
15348 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15349 close.on('click', this.removeBtnClick, this, close);
15353 * fix the bug in Safari iOS8
15355 this.inputEl().on("focus", function(e){
15356 document.activeElement.blur();
15359 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15366 renderTouchView : function()
15368 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15369 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15371 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15372 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15374 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15375 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15376 this.touchViewBodyEl.setStyle('overflow', 'auto');
15378 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15379 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15381 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15382 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15386 showTouchView : function()
15392 this.touchViewHeaderEl.hide();
15394 if(this.modalTitle.length){
15395 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15396 this.touchViewHeaderEl.show();
15399 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15400 this.touchViewEl.show();
15402 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15404 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15405 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15407 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15409 if(this.modalTitle.length){
15410 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15413 this.touchViewBodyEl.setHeight(bodyHeight);
15417 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15419 this.touchViewEl.addClass('in');
15422 if(this._touchViewMask){
15423 Roo.get(document.body).addClass("x-body-masked");
15424 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15425 this._touchViewMask.setStyle('z-index', 10000);
15426 this._touchViewMask.addClass('show');
15429 this.doTouchViewQuery();
15433 hideTouchView : function()
15435 this.touchViewEl.removeClass('in');
15439 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15441 this.touchViewEl.setStyle('display', 'none');
15444 if(this._touchViewMask){
15445 this._touchViewMask.removeClass('show');
15446 Roo.get(document.body).removeClass("x-body-masked");
15450 setTouchViewValue : function()
15457 Roo.each(this.tickItems, function(o){
15462 this.hideTouchView();
15465 doTouchViewQuery : function()
15474 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15478 if(!this.alwaysQuery || this.mode == 'local'){
15479 this.onTouchViewLoad();
15486 onTouchViewBeforeLoad : function(combo,opts)
15492 onTouchViewLoad : function()
15494 if(this.store.getCount() < 1){
15495 this.onTouchViewEmptyResults();
15499 this.clearTouchView();
15501 var rawValue = this.getRawValue();
15503 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15505 this.tickItems = [];
15507 this.store.data.each(function(d, rowIndex){
15508 var row = this.touchViewListGroup.createChild(template);
15510 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15511 row.addClass(d.data.cls);
15514 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15517 html : d.data[this.displayField]
15520 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15521 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15524 row.removeClass('selected');
15525 if(!this.multiple && this.valueField &&
15526 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15529 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15530 row.addClass('selected');
15533 if(this.multiple && this.valueField &&
15534 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15538 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15539 this.tickItems.push(d.data);
15542 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15546 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15548 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15550 if(this.modalTitle.length){
15551 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15554 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15556 if(this.mobile_restrict_height && listHeight < bodyHeight){
15557 this.touchViewBodyEl.setHeight(listHeight);
15562 if(firstChecked && listHeight > bodyHeight){
15563 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15568 onTouchViewLoadException : function()
15570 this.hideTouchView();
15573 onTouchViewEmptyResults : function()
15575 this.clearTouchView();
15577 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15579 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15583 clearTouchView : function()
15585 this.touchViewListGroup.dom.innerHTML = '';
15588 onTouchViewClick : function(e, el, o)
15590 e.preventDefault();
15593 var rowIndex = o.rowIndex;
15595 var r = this.store.getAt(rowIndex);
15597 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15599 if(!this.multiple){
15600 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15601 c.dom.removeAttribute('checked');
15604 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15606 this.setFromData(r.data);
15608 var close = this.closeTriggerEl();
15614 this.hideTouchView();
15616 this.fireEvent('select', this, r, rowIndex);
15621 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15622 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15623 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15627 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15628 this.addItem(r.data);
15629 this.tickItems.push(r.data);
15633 getAutoCreateNativeIOS : function()
15636 cls: 'form-group' //input-group,
15641 cls : 'roo-ios-select'
15645 combobox.name = this.name;
15648 if (this.disabled) {
15649 combobox.disabled = true;
15652 var settings = this;
15654 ['xs','sm','md','lg'].map(function(size){
15655 if (settings[size]) {
15656 cfg.cls += ' col-' + size + '-' + settings[size];
15666 initIOSView : function()
15668 this.store.on('load', this.onIOSViewLoad, this);
15673 onIOSViewLoad : function()
15675 if(this.store.getCount() < 1){
15679 this.clearIOSView();
15681 if(this.allowBlank) {
15683 var default_text = '-- SELECT --';
15685 if(this.placeholder.length){
15686 default_text = this.placeholder;
15689 if(this.emptyTitle.length){
15690 default_text += ' - ' + this.emptyTitle + ' -';
15693 var opt = this.inputEl().createChild({
15696 html : default_text
15700 o[this.valueField] = 0;
15701 o[this.displayField] = default_text;
15703 this.ios_options.push({
15710 this.store.data.each(function(d, rowIndex){
15714 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15715 html = d.data[this.displayField];
15720 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15721 value = d.data[this.valueField];
15730 if(this.value == d.data[this.valueField]){
15731 option['selected'] = true;
15734 var opt = this.inputEl().createChild(option);
15736 this.ios_options.push({
15743 this.inputEl().on('change', function(){
15744 this.fireEvent('select', this);
15749 clearIOSView: function()
15751 this.inputEl().dom.innerHTML = '';
15753 this.ios_options = [];
15756 setIOSValue: function(v)
15760 if(!this.ios_options){
15764 Roo.each(this.ios_options, function(opts){
15766 opts.el.dom.removeAttribute('selected');
15768 if(opts.data[this.valueField] != v){
15772 opts.el.dom.setAttribute('selected', true);
15778 * @cfg {Boolean} grow
15782 * @cfg {Number} growMin
15786 * @cfg {Number} growMax
15795 Roo.apply(Roo.bootstrap.ComboBox, {
15799 cls: 'modal-header',
15821 cls: 'list-group-item',
15825 cls: 'roo-combobox-list-group-item-value'
15829 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15843 listItemCheckbox : {
15845 cls: 'list-group-item',
15849 cls: 'roo-combobox-list-group-item-value'
15853 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15869 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15874 cls: 'modal-footer',
15882 cls: 'col-xs-6 text-left',
15885 cls: 'btn btn-danger roo-touch-view-cancel',
15891 cls: 'col-xs-6 text-right',
15894 cls: 'btn btn-success roo-touch-view-ok',
15905 Roo.apply(Roo.bootstrap.ComboBox, {
15907 touchViewTemplate : {
15909 cls: 'modal fade roo-combobox-touch-view',
15913 cls: 'modal-dialog',
15914 style : 'position:fixed', // we have to fix position....
15918 cls: 'modal-content',
15920 Roo.bootstrap.ComboBox.header,
15921 Roo.bootstrap.ComboBox.body,
15922 Roo.bootstrap.ComboBox.footer
15931 * Ext JS Library 1.1.1
15932 * Copyright(c) 2006-2007, Ext JS, LLC.
15934 * Originally Released Under LGPL - original licence link has changed is not relivant.
15937 * <script type="text/javascript">
15942 * @extends Roo.util.Observable
15943 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
15944 * This class also supports single and multi selection modes. <br>
15945 * Create a data model bound view:
15947 var store = new Roo.data.Store(...);
15949 var view = new Roo.View({
15951 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
15953 singleSelect: true,
15954 selectedClass: "ydataview-selected",
15958 // listen for node click?
15959 view.on("click", function(vw, index, node, e){
15960 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15964 dataModel.load("foobar.xml");
15966 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15968 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15969 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15971 * Note: old style constructor is still suported (container, template, config)
15974 * Create a new View
15975 * @param {Object} config The config object
15978 Roo.View = function(config, depreciated_tpl, depreciated_config){
15980 this.parent = false;
15982 if (typeof(depreciated_tpl) == 'undefined') {
15983 // new way.. - universal constructor.
15984 Roo.apply(this, config);
15985 this.el = Roo.get(this.el);
15988 this.el = Roo.get(config);
15989 this.tpl = depreciated_tpl;
15990 Roo.apply(this, depreciated_config);
15992 this.wrapEl = this.el.wrap().wrap();
15993 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15996 if(typeof(this.tpl) == "string"){
15997 this.tpl = new Roo.Template(this.tpl);
15999 // support xtype ctors..
16000 this.tpl = new Roo.factory(this.tpl, Roo);
16004 this.tpl.compile();
16009 * @event beforeclick
16010 * Fires before a click is processed. Returns false to cancel the default action.
16011 * @param {Roo.View} this
16012 * @param {Number} index The index of the target node
16013 * @param {HTMLElement} node The target node
16014 * @param {Roo.EventObject} e The raw event object
16016 "beforeclick" : true,
16019 * Fires when a template node is clicked.
16020 * @param {Roo.View} this
16021 * @param {Number} index The index of the target node
16022 * @param {HTMLElement} node The target node
16023 * @param {Roo.EventObject} e The raw event object
16028 * Fires when a template node is double clicked.
16029 * @param {Roo.View} this
16030 * @param {Number} index The index of the target node
16031 * @param {HTMLElement} node The target node
16032 * @param {Roo.EventObject} e The raw event object
16036 * @event contextmenu
16037 * Fires when a template node is right clicked.
16038 * @param {Roo.View} this
16039 * @param {Number} index The index of the target node
16040 * @param {HTMLElement} node The target node
16041 * @param {Roo.EventObject} e The raw event object
16043 "contextmenu" : true,
16045 * @event selectionchange
16046 * Fires when the selected nodes change.
16047 * @param {Roo.View} this
16048 * @param {Array} selections Array of the selected nodes
16050 "selectionchange" : true,
16053 * @event beforeselect
16054 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16055 * @param {Roo.View} this
16056 * @param {HTMLElement} node The node to be selected
16057 * @param {Array} selections Array of currently selected nodes
16059 "beforeselect" : true,
16061 * @event preparedata
16062 * Fires on every row to render, to allow you to change the data.
16063 * @param {Roo.View} this
16064 * @param {Object} data to be rendered (change this)
16066 "preparedata" : true
16074 "click": this.onClick,
16075 "dblclick": this.onDblClick,
16076 "contextmenu": this.onContextMenu,
16080 this.selections = [];
16082 this.cmp = new Roo.CompositeElementLite([]);
16084 this.store = Roo.factory(this.store, Roo.data);
16085 this.setStore(this.store, true);
16088 if ( this.footer && this.footer.xtype) {
16090 var fctr = this.wrapEl.appendChild(document.createElement("div"));
16092 this.footer.dataSource = this.store;
16093 this.footer.container = fctr;
16094 this.footer = Roo.factory(this.footer, Roo);
16095 fctr.insertFirst(this.el);
16097 // this is a bit insane - as the paging toolbar seems to detach the el..
16098 // dom.parentNode.parentNode.parentNode
16099 // they get detached?
16103 Roo.View.superclass.constructor.call(this);
16108 Roo.extend(Roo.View, Roo.util.Observable, {
16111 * @cfg {Roo.data.Store} store Data store to load data from.
16116 * @cfg {String|Roo.Element} el The container element.
16121 * @cfg {String|Roo.Template} tpl The template used by this View
16125 * @cfg {String} dataName the named area of the template to use as the data area
16126 * Works with domtemplates roo-name="name"
16130 * @cfg {String} selectedClass The css class to add to selected nodes
16132 selectedClass : "x-view-selected",
16134 * @cfg {String} emptyText The empty text to show when nothing is loaded.
16139 * @cfg {String} text to display on mask (default Loading)
16143 * @cfg {Boolean} multiSelect Allow multiple selection
16145 multiSelect : false,
16147 * @cfg {Boolean} singleSelect Allow single selection
16149 singleSelect: false,
16152 * @cfg {Boolean} toggleSelect - selecting
16154 toggleSelect : false,
16157 * @cfg {Boolean} tickable - selecting
16162 * Returns the element this view is bound to.
16163 * @return {Roo.Element}
16165 getEl : function(){
16166 return this.wrapEl;
16172 * Refreshes the view. - called by datachanged on the store. - do not call directly.
16174 refresh : function(){
16175 //Roo.log('refresh');
16178 // if we are using something like 'domtemplate', then
16179 // the what gets used is:
16180 // t.applySubtemplate(NAME, data, wrapping data..)
16181 // the outer template then get' applied with
16182 // the store 'extra data'
16183 // and the body get's added to the
16184 // roo-name="data" node?
16185 // <span class='roo-tpl-{name}'></span> ?????
16189 this.clearSelections();
16190 this.el.update("");
16192 var records = this.store.getRange();
16193 if(records.length < 1) {
16195 // is this valid?? = should it render a template??
16197 this.el.update(this.emptyText);
16201 if (this.dataName) {
16202 this.el.update(t.apply(this.store.meta)); //????
16203 el = this.el.child('.roo-tpl-' + this.dataName);
16206 for(var i = 0, len = records.length; i < len; i++){
16207 var data = this.prepareData(records[i].data, i, records[i]);
16208 this.fireEvent("preparedata", this, data, i, records[i]);
16210 var d = Roo.apply({}, data);
16213 Roo.apply(d, {'roo-id' : Roo.id()});
16217 Roo.each(this.parent.item, function(item){
16218 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16221 Roo.apply(d, {'roo-data-checked' : 'checked'});
16225 html[html.length] = Roo.util.Format.trim(
16227 t.applySubtemplate(this.dataName, d, this.store.meta) :
16234 el.update(html.join(""));
16235 this.nodes = el.dom.childNodes;
16236 this.updateIndexes(0);
16241 * Function to override to reformat the data that is sent to
16242 * the template for each node.
16243 * DEPRICATED - use the preparedata event handler.
16244 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16245 * a JSON object for an UpdateManager bound view).
16247 prepareData : function(data, index, record)
16249 this.fireEvent("preparedata", this, data, index, record);
16253 onUpdate : function(ds, record){
16254 // Roo.log('on update');
16255 this.clearSelections();
16256 var index = this.store.indexOf(record);
16257 var n = this.nodes[index];
16258 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16259 n.parentNode.removeChild(n);
16260 this.updateIndexes(index, index);
16266 onAdd : function(ds, records, index)
16268 //Roo.log(['on Add', ds, records, index] );
16269 this.clearSelections();
16270 if(this.nodes.length == 0){
16274 var n = this.nodes[index];
16275 for(var i = 0, len = records.length; i < len; i++){
16276 var d = this.prepareData(records[i].data, i, records[i]);
16278 this.tpl.insertBefore(n, d);
16281 this.tpl.append(this.el, d);
16284 this.updateIndexes(index);
16287 onRemove : function(ds, record, index){
16288 // Roo.log('onRemove');
16289 this.clearSelections();
16290 var el = this.dataName ?
16291 this.el.child('.roo-tpl-' + this.dataName) :
16294 el.dom.removeChild(this.nodes[index]);
16295 this.updateIndexes(index);
16299 * Refresh an individual node.
16300 * @param {Number} index
16302 refreshNode : function(index){
16303 this.onUpdate(this.store, this.store.getAt(index));
16306 updateIndexes : function(startIndex, endIndex){
16307 var ns = this.nodes;
16308 startIndex = startIndex || 0;
16309 endIndex = endIndex || ns.length - 1;
16310 for(var i = startIndex; i <= endIndex; i++){
16311 ns[i].nodeIndex = i;
16316 * Changes the data store this view uses and refresh the view.
16317 * @param {Store} store
16319 setStore : function(store, initial){
16320 if(!initial && this.store){
16321 this.store.un("datachanged", this.refresh);
16322 this.store.un("add", this.onAdd);
16323 this.store.un("remove", this.onRemove);
16324 this.store.un("update", this.onUpdate);
16325 this.store.un("clear", this.refresh);
16326 this.store.un("beforeload", this.onBeforeLoad);
16327 this.store.un("load", this.onLoad);
16328 this.store.un("loadexception", this.onLoad);
16332 store.on("datachanged", this.refresh, this);
16333 store.on("add", this.onAdd, this);
16334 store.on("remove", this.onRemove, this);
16335 store.on("update", this.onUpdate, this);
16336 store.on("clear", this.refresh, this);
16337 store.on("beforeload", this.onBeforeLoad, this);
16338 store.on("load", this.onLoad, this);
16339 store.on("loadexception", this.onLoad, this);
16347 * onbeforeLoad - masks the loading area.
16350 onBeforeLoad : function(store,opts)
16352 //Roo.log('onBeforeLoad');
16354 this.el.update("");
16356 this.el.mask(this.mask ? this.mask : "Loading" );
16358 onLoad : function ()
16365 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16366 * @param {HTMLElement} node
16367 * @return {HTMLElement} The template node
16369 findItemFromChild : function(node){
16370 var el = this.dataName ?
16371 this.el.child('.roo-tpl-' + this.dataName,true) :
16374 if(!node || node.parentNode == el){
16377 var p = node.parentNode;
16378 while(p && p != el){
16379 if(p.parentNode == el){
16388 onClick : function(e){
16389 var item = this.findItemFromChild(e.getTarget());
16391 var index = this.indexOf(item);
16392 if(this.onItemClick(item, index, e) !== false){
16393 this.fireEvent("click", this, index, item, e);
16396 this.clearSelections();
16401 onContextMenu : function(e){
16402 var item = this.findItemFromChild(e.getTarget());
16404 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16409 onDblClick : function(e){
16410 var item = this.findItemFromChild(e.getTarget());
16412 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16416 onItemClick : function(item, index, e)
16418 if(this.fireEvent("beforeclick", this, index, item, e) === false){
16421 if (this.toggleSelect) {
16422 var m = this.isSelected(item) ? 'unselect' : 'select';
16425 _t[m](item, true, false);
16428 if(this.multiSelect || this.singleSelect){
16429 if(this.multiSelect && e.shiftKey && this.lastSelection){
16430 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16432 this.select(item, this.multiSelect && e.ctrlKey);
16433 this.lastSelection = item;
16436 if(!this.tickable){
16437 e.preventDefault();
16445 * Get the number of selected nodes.
16448 getSelectionCount : function(){
16449 return this.selections.length;
16453 * Get the currently selected nodes.
16454 * @return {Array} An array of HTMLElements
16456 getSelectedNodes : function(){
16457 return this.selections;
16461 * Get the indexes of the selected nodes.
16464 getSelectedIndexes : function(){
16465 var indexes = [], s = this.selections;
16466 for(var i = 0, len = s.length; i < len; i++){
16467 indexes.push(s[i].nodeIndex);
16473 * Clear all selections
16474 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16476 clearSelections : function(suppressEvent){
16477 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16478 this.cmp.elements = this.selections;
16479 this.cmp.removeClass(this.selectedClass);
16480 this.selections = [];
16481 if(!suppressEvent){
16482 this.fireEvent("selectionchange", this, this.selections);
16488 * Returns true if the passed node is selected
16489 * @param {HTMLElement/Number} node The node or node index
16490 * @return {Boolean}
16492 isSelected : function(node){
16493 var s = this.selections;
16497 node = this.getNode(node);
16498 return s.indexOf(node) !== -1;
16503 * @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
16504 * @param {Boolean} keepExisting (optional) true to keep existing selections
16505 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16507 select : function(nodeInfo, keepExisting, suppressEvent){
16508 if(nodeInfo instanceof Array){
16510 this.clearSelections(true);
16512 for(var i = 0, len = nodeInfo.length; i < len; i++){
16513 this.select(nodeInfo[i], true, true);
16517 var node = this.getNode(nodeInfo);
16518 if(!node || this.isSelected(node)){
16519 return; // already selected.
16522 this.clearSelections(true);
16525 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16526 Roo.fly(node).addClass(this.selectedClass);
16527 this.selections.push(node);
16528 if(!suppressEvent){
16529 this.fireEvent("selectionchange", this, this.selections);
16537 * @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
16538 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16539 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16541 unselect : function(nodeInfo, keepExisting, suppressEvent)
16543 if(nodeInfo instanceof Array){
16544 Roo.each(this.selections, function(s) {
16545 this.unselect(s, nodeInfo);
16549 var node = this.getNode(nodeInfo);
16550 if(!node || !this.isSelected(node)){
16551 //Roo.log("not selected");
16552 return; // not selected.
16556 Roo.each(this.selections, function(s) {
16558 Roo.fly(node).removeClass(this.selectedClass);
16565 this.selections= ns;
16566 this.fireEvent("selectionchange", this, this.selections);
16570 * Gets a template node.
16571 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16572 * @return {HTMLElement} The node or null if it wasn't found
16574 getNode : function(nodeInfo){
16575 if(typeof nodeInfo == "string"){
16576 return document.getElementById(nodeInfo);
16577 }else if(typeof nodeInfo == "number"){
16578 return this.nodes[nodeInfo];
16584 * Gets a range template nodes.
16585 * @param {Number} startIndex
16586 * @param {Number} endIndex
16587 * @return {Array} An array of nodes
16589 getNodes : function(start, end){
16590 var ns = this.nodes;
16591 start = start || 0;
16592 end = typeof end == "undefined" ? ns.length - 1 : end;
16595 for(var i = start; i <= end; i++){
16599 for(var i = start; i >= end; i--){
16607 * Finds the index of the passed node
16608 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16609 * @return {Number} The index of the node or -1
16611 indexOf : function(node){
16612 node = this.getNode(node);
16613 if(typeof node.nodeIndex == "number"){
16614 return node.nodeIndex;
16616 var ns = this.nodes;
16617 for(var i = 0, len = ns.length; i < len; i++){
16628 * based on jquery fullcalendar
16632 Roo.bootstrap = Roo.bootstrap || {};
16634 * @class Roo.bootstrap.Calendar
16635 * @extends Roo.bootstrap.Component
16636 * Bootstrap Calendar class
16637 * @cfg {Boolean} loadMask (true|false) default false
16638 * @cfg {Object} header generate the user specific header of the calendar, default false
16641 * Create a new Container
16642 * @param {Object} config The config object
16647 Roo.bootstrap.Calendar = function(config){
16648 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16652 * Fires when a date is selected
16653 * @param {DatePicker} this
16654 * @param {Date} date The selected date
16658 * @event monthchange
16659 * Fires when the displayed month changes
16660 * @param {DatePicker} this
16661 * @param {Date} date The selected month
16663 'monthchange': true,
16665 * @event evententer
16666 * Fires when mouse over an event
16667 * @param {Calendar} this
16668 * @param {event} Event
16670 'evententer': true,
16672 * @event eventleave
16673 * Fires when the mouse leaves an
16674 * @param {Calendar} this
16677 'eventleave': true,
16679 * @event eventclick
16680 * Fires when the mouse click an
16681 * @param {Calendar} this
16690 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
16693 * @cfg {Number} startDay
16694 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16702 getAutoCreate : function(){
16705 var fc_button = function(name, corner, style, content ) {
16706 return Roo.apply({},{
16708 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
16710 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16713 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16724 style : 'width:100%',
16731 cls : 'fc-header-left',
16733 fc_button('prev', 'left', 'arrow', '‹' ),
16734 fc_button('next', 'right', 'arrow', '›' ),
16735 { tag: 'span', cls: 'fc-header-space' },
16736 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
16744 cls : 'fc-header-center',
16748 cls: 'fc-header-title',
16751 html : 'month / year'
16759 cls : 'fc-header-right',
16761 /* fc_button('month', 'left', '', 'month' ),
16762 fc_button('week', '', '', 'week' ),
16763 fc_button('day', 'right', '', 'day' )
16775 header = this.header;
16778 var cal_heads = function() {
16780 // fixme - handle this.
16782 for (var i =0; i < Date.dayNames.length; i++) {
16783 var d = Date.dayNames[i];
16786 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16787 html : d.substring(0,3)
16791 ret[0].cls += ' fc-first';
16792 ret[6].cls += ' fc-last';
16795 var cal_cell = function(n) {
16798 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16803 cls: 'fc-day-number',
16807 cls: 'fc-day-content',
16811 style: 'position: relative;' // height: 17px;
16823 var cal_rows = function() {
16826 for (var r = 0; r < 6; r++) {
16833 for (var i =0; i < Date.dayNames.length; i++) {
16834 var d = Date.dayNames[i];
16835 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16838 row.cn[0].cls+=' fc-first';
16839 row.cn[0].cn[0].style = 'min-height:90px';
16840 row.cn[6].cls+=' fc-last';
16844 ret[0].cls += ' fc-first';
16845 ret[4].cls += ' fc-prev-last';
16846 ret[5].cls += ' fc-last';
16853 cls: 'fc-border-separate',
16854 style : 'width:100%',
16862 cls : 'fc-first fc-last',
16880 cls : 'fc-content',
16881 style : "position: relative;",
16884 cls : 'fc-view fc-view-month fc-grid',
16885 style : 'position: relative',
16886 unselectable : 'on',
16889 cls : 'fc-event-container',
16890 style : 'position:absolute;z-index:8;top:0;left:0;'
16908 initEvents : function()
16911 throw "can not find store for calendar";
16917 style: "text-align:center",
16921 style: "background-color:white;width:50%;margin:250 auto",
16925 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
16936 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16938 var size = this.el.select('.fc-content', true).first().getSize();
16939 this.maskEl.setSize(size.width, size.height);
16940 this.maskEl.enableDisplayMode("block");
16941 if(!this.loadMask){
16942 this.maskEl.hide();
16945 this.store = Roo.factory(this.store, Roo.data);
16946 this.store.on('load', this.onLoad, this);
16947 this.store.on('beforeload', this.onBeforeLoad, this);
16951 this.cells = this.el.select('.fc-day',true);
16952 //Roo.log(this.cells);
16953 this.textNodes = this.el.query('.fc-day-number');
16954 this.cells.addClassOnOver('fc-state-hover');
16956 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16957 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16958 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16959 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16961 this.on('monthchange', this.onMonthChange, this);
16963 this.update(new Date().clearTime());
16966 resize : function() {
16967 var sz = this.el.getSize();
16969 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16970 this.el.select('.fc-day-content div',true).setHeight(34);
16975 showPrevMonth : function(e){
16976 this.update(this.activeDate.add("mo", -1));
16978 showToday : function(e){
16979 this.update(new Date().clearTime());
16982 showNextMonth : function(e){
16983 this.update(this.activeDate.add("mo", 1));
16987 showPrevYear : function(){
16988 this.update(this.activeDate.add("y", -1));
16992 showNextYear : function(){
16993 this.update(this.activeDate.add("y", 1));
16998 update : function(date)
17000 var vd = this.activeDate;
17001 this.activeDate = date;
17002 // if(vd && this.el){
17003 // var t = date.getTime();
17004 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17005 // Roo.log('using add remove');
17007 // this.fireEvent('monthchange', this, date);
17009 // this.cells.removeClass("fc-state-highlight");
17010 // this.cells.each(function(c){
17011 // if(c.dateValue == t){
17012 // c.addClass("fc-state-highlight");
17013 // setTimeout(function(){
17014 // try{c.dom.firstChild.focus();}catch(e){}
17024 var days = date.getDaysInMonth();
17026 var firstOfMonth = date.getFirstDateOfMonth();
17027 var startingPos = firstOfMonth.getDay()-this.startDay;
17029 if(startingPos < this.startDay){
17033 var pm = date.add(Date.MONTH, -1);
17034 var prevStart = pm.getDaysInMonth()-startingPos;
17036 this.cells = this.el.select('.fc-day',true);
17037 this.textNodes = this.el.query('.fc-day-number');
17038 this.cells.addClassOnOver('fc-state-hover');
17040 var cells = this.cells.elements;
17041 var textEls = this.textNodes;
17043 Roo.each(cells, function(cell){
17044 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17047 days += startingPos;
17049 // convert everything to numbers so it's fast
17050 var day = 86400000;
17051 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17054 //Roo.log(prevStart);
17056 var today = new Date().clearTime().getTime();
17057 var sel = date.clearTime().getTime();
17058 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17059 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17060 var ddMatch = this.disabledDatesRE;
17061 var ddText = this.disabledDatesText;
17062 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17063 var ddaysText = this.disabledDaysText;
17064 var format = this.format;
17066 var setCellClass = function(cal, cell){
17070 //Roo.log('set Cell Class');
17072 var t = d.getTime();
17076 cell.dateValue = t;
17078 cell.className += " fc-today";
17079 cell.className += " fc-state-highlight";
17080 cell.title = cal.todayText;
17083 // disable highlight in other month..
17084 //cell.className += " fc-state-highlight";
17089 cell.className = " fc-state-disabled";
17090 cell.title = cal.minText;
17094 cell.className = " fc-state-disabled";
17095 cell.title = cal.maxText;
17099 if(ddays.indexOf(d.getDay()) != -1){
17100 cell.title = ddaysText;
17101 cell.className = " fc-state-disabled";
17104 if(ddMatch && format){
17105 var fvalue = d.dateFormat(format);
17106 if(ddMatch.test(fvalue)){
17107 cell.title = ddText.replace("%0", fvalue);
17108 cell.className = " fc-state-disabled";
17112 if (!cell.initialClassName) {
17113 cell.initialClassName = cell.dom.className;
17116 cell.dom.className = cell.initialClassName + ' ' + cell.className;
17121 for(; i < startingPos; i++) {
17122 textEls[i].innerHTML = (++prevStart);
17123 d.setDate(d.getDate()+1);
17125 cells[i].className = "fc-past fc-other-month";
17126 setCellClass(this, cells[i]);
17131 for(; i < days; i++){
17132 intDay = i - startingPos + 1;
17133 textEls[i].innerHTML = (intDay);
17134 d.setDate(d.getDate()+1);
17136 cells[i].className = ''; // "x-date-active";
17137 setCellClass(this, cells[i]);
17141 for(; i < 42; i++) {
17142 textEls[i].innerHTML = (++extraDays);
17143 d.setDate(d.getDate()+1);
17145 cells[i].className = "fc-future fc-other-month";
17146 setCellClass(this, cells[i]);
17149 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17151 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17153 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17154 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17156 if(totalRows != 6){
17157 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17158 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17161 this.fireEvent('monthchange', this, date);
17165 if(!this.internalRender){
17166 var main = this.el.dom.firstChild;
17167 var w = main.offsetWidth;
17168 this.el.setWidth(w + this.el.getBorderWidth("lr"));
17169 Roo.fly(main).setWidth(w);
17170 this.internalRender = true;
17171 // opera does not respect the auto grow header center column
17172 // then, after it gets a width opera refuses to recalculate
17173 // without a second pass
17174 if(Roo.isOpera && !this.secondPass){
17175 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17176 this.secondPass = true;
17177 this.update.defer(10, this, [date]);
17184 findCell : function(dt) {
17185 dt = dt.clearTime().getTime();
17187 this.cells.each(function(c){
17188 //Roo.log("check " +c.dateValue + '?=' + dt);
17189 if(c.dateValue == dt){
17199 findCells : function(ev) {
17200 var s = ev.start.clone().clearTime().getTime();
17202 var e= ev.end.clone().clearTime().getTime();
17205 this.cells.each(function(c){
17206 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17208 if(c.dateValue > e){
17211 if(c.dateValue < s){
17220 // findBestRow: function(cells)
17224 // for (var i =0 ; i < cells.length;i++) {
17225 // ret = Math.max(cells[i].rows || 0,ret);
17232 addItem : function(ev)
17234 // look for vertical location slot in
17235 var cells = this.findCells(ev);
17237 // ev.row = this.findBestRow(cells);
17239 // work out the location.
17243 for(var i =0; i < cells.length; i++) {
17245 cells[i].row = cells[0].row;
17248 cells[i].row = cells[i].row + 1;
17258 if (crow.start.getY() == cells[i].getY()) {
17260 crow.end = cells[i];
17277 cells[0].events.push(ev);
17279 this.calevents.push(ev);
17282 clearEvents: function() {
17284 if(!this.calevents){
17288 Roo.each(this.cells.elements, function(c){
17294 Roo.each(this.calevents, function(e) {
17295 Roo.each(e.els, function(el) {
17296 el.un('mouseenter' ,this.onEventEnter, this);
17297 el.un('mouseleave' ,this.onEventLeave, this);
17302 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17308 renderEvents: function()
17312 this.cells.each(function(c) {
17321 if(c.row != c.events.length){
17322 r = 4 - (4 - (c.row - c.events.length));
17325 c.events = ev.slice(0, r);
17326 c.more = ev.slice(r);
17328 if(c.more.length && c.more.length == 1){
17329 c.events.push(c.more.pop());
17332 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17336 this.cells.each(function(c) {
17338 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17341 for (var e = 0; e < c.events.length; e++){
17342 var ev = c.events[e];
17343 var rows = ev.rows;
17345 for(var i = 0; i < rows.length; i++) {
17347 // how many rows should it span..
17350 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17351 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17353 unselectable : "on",
17356 cls: 'fc-event-inner',
17360 // cls: 'fc-event-time',
17361 // html : cells.length > 1 ? '' : ev.time
17365 cls: 'fc-event-title',
17366 html : String.format('{0}', ev.title)
17373 cls: 'ui-resizable-handle ui-resizable-e',
17374 html : '  '
17381 cfg.cls += ' fc-event-start';
17383 if ((i+1) == rows.length) {
17384 cfg.cls += ' fc-event-end';
17387 var ctr = _this.el.select('.fc-event-container',true).first();
17388 var cg = ctr.createChild(cfg);
17390 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17391 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17393 var r = (c.more.length) ? 1 : 0;
17394 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
17395 cg.setWidth(ebox.right - sbox.x -2);
17397 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17398 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17399 cg.on('click', _this.onEventClick, _this, ev);
17410 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17411 style : 'position: absolute',
17412 unselectable : "on",
17415 cls: 'fc-event-inner',
17419 cls: 'fc-event-title',
17427 cls: 'ui-resizable-handle ui-resizable-e',
17428 html : '  '
17434 var ctr = _this.el.select('.fc-event-container',true).first();
17435 var cg = ctr.createChild(cfg);
17437 var sbox = c.select('.fc-day-content',true).first().getBox();
17438 var ebox = c.select('.fc-day-content',true).first().getBox();
17440 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
17441 cg.setWidth(ebox.right - sbox.x -2);
17443 cg.on('click', _this.onMoreEventClick, _this, c.more);
17453 onEventEnter: function (e, el,event,d) {
17454 this.fireEvent('evententer', this, el, event);
17457 onEventLeave: function (e, el,event,d) {
17458 this.fireEvent('eventleave', this, el, event);
17461 onEventClick: function (e, el,event,d) {
17462 this.fireEvent('eventclick', this, el, event);
17465 onMonthChange: function () {
17469 onMoreEventClick: function(e, el, more)
17473 this.calpopover.placement = 'right';
17474 this.calpopover.setTitle('More');
17476 this.calpopover.setContent('');
17478 var ctr = this.calpopover.el.select('.popover-content', true).first();
17480 Roo.each(more, function(m){
17482 cls : 'fc-event-hori fc-event-draggable',
17485 var cg = ctr.createChild(cfg);
17487 cg.on('click', _this.onEventClick, _this, m);
17490 this.calpopover.show(el);
17495 onLoad: function ()
17497 this.calevents = [];
17500 if(this.store.getCount() > 0){
17501 this.store.data.each(function(d){
17504 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17505 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17506 time : d.data.start_time,
17507 title : d.data.title,
17508 description : d.data.description,
17509 venue : d.data.venue
17514 this.renderEvents();
17516 if(this.calevents.length && this.loadMask){
17517 this.maskEl.hide();
17521 onBeforeLoad: function()
17523 this.clearEvents();
17525 this.maskEl.show();
17539 * @class Roo.bootstrap.Popover
17540 * @extends Roo.bootstrap.Component
17541 * Bootstrap Popover class
17542 * @cfg {String} html contents of the popover (or false to use children..)
17543 * @cfg {String} title of popover (or false to hide)
17544 * @cfg {String} placement how it is placed
17545 * @cfg {String} trigger click || hover (or false to trigger manually)
17546 * @cfg {String} over what (parent or false to trigger manually.)
17547 * @cfg {Number} delay - delay before showing
17550 * Create a new Popover
17551 * @param {Object} config The config object
17554 Roo.bootstrap.Popover = function(config){
17555 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17561 * After the popover show
17563 * @param {Roo.bootstrap.Popover} this
17568 * After the popover hide
17570 * @param {Roo.bootstrap.Popover} this
17576 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
17578 title: 'Fill in a title',
17581 placement : 'right',
17582 trigger : 'hover', // hover
17588 can_build_overlaid : false,
17590 getChildContainer : function()
17592 return this.el.select('.popover-content',true).first();
17595 getAutoCreate : function(){
17598 cls : 'popover roo-dynamic',
17599 style: 'display:block',
17605 cls : 'popover-inner',
17609 cls: 'popover-title',
17613 cls : 'popover-content',
17624 setTitle: function(str)
17627 this.el.select('.popover-title',true).first().dom.innerHTML = str;
17629 setContent: function(str)
17632 this.el.select('.popover-content',true).first().dom.innerHTML = str;
17634 // as it get's added to the bottom of the page.
17635 onRender : function(ct, position)
17637 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17639 var cfg = Roo.apply({}, this.getAutoCreate());
17643 cfg.cls += ' ' + this.cls;
17646 cfg.style = this.style;
17648 //Roo.log("adding to ");
17649 this.el = Roo.get(document.body).createChild(cfg, position);
17650 // Roo.log(this.el);
17655 initEvents : function()
17657 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17658 this.el.enableDisplayMode('block');
17660 if (this.over === false) {
17663 if (this.triggers === false) {
17666 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17667 var triggers = this.trigger ? this.trigger.split(' ') : [];
17668 Roo.each(triggers, function(trigger) {
17670 if (trigger == 'click') {
17671 on_el.on('click', this.toggle, this);
17672 } else if (trigger != 'manual') {
17673 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
17674 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17676 on_el.on(eventIn ,this.enter, this);
17677 on_el.on(eventOut, this.leave, this);
17688 toggle : function () {
17689 this.hoverState == 'in' ? this.leave() : this.enter();
17692 enter : function () {
17694 clearTimeout(this.timeout);
17696 this.hoverState = 'in';
17698 if (!this.delay || !this.delay.show) {
17703 this.timeout = setTimeout(function () {
17704 if (_t.hoverState == 'in') {
17707 }, this.delay.show)
17710 leave : function() {
17711 clearTimeout(this.timeout);
17713 this.hoverState = 'out';
17715 if (!this.delay || !this.delay.hide) {
17720 this.timeout = setTimeout(function () {
17721 if (_t.hoverState == 'out') {
17724 }, this.delay.hide)
17727 show : function (on_el)
17730 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17734 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17735 if (this.html !== false) {
17736 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17738 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17739 if (!this.title.length) {
17740 this.el.select('.popover-title',true).hide();
17743 var placement = typeof this.placement == 'function' ?
17744 this.placement.call(this, this.el, on_el) :
17747 var autoToken = /\s?auto?\s?/i;
17748 var autoPlace = autoToken.test(placement);
17750 placement = placement.replace(autoToken, '') || 'top';
17754 //this.el.setXY([0,0]);
17756 this.el.dom.style.display='block';
17757 this.el.addClass(placement);
17759 //this.el.appendTo(on_el);
17761 var p = this.getPosition();
17762 var box = this.el.getBox();
17767 var align = Roo.bootstrap.Popover.alignment[placement];
17770 this.el.alignTo(on_el, align[0],align[1]);
17771 //var arrow = this.el.select('.arrow',true).first();
17772 //arrow.set(align[2],
17774 this.el.addClass('in');
17777 if (this.el.hasClass('fade')) {
17781 this.hoverState = 'in';
17783 this.fireEvent('show', this);
17788 this.el.setXY([0,0]);
17789 this.el.removeClass('in');
17791 this.hoverState = null;
17793 this.fireEvent('hide', this);
17798 Roo.bootstrap.Popover.alignment = {
17799 'left' : ['r-l', [-10,0], 'right'],
17800 'right' : ['l-r', [10,0], 'left'],
17801 'bottom' : ['t-b', [0,10], 'top'],
17802 'top' : [ 'b-t', [0,-10], 'bottom']
17813 * @class Roo.bootstrap.Progress
17814 * @extends Roo.bootstrap.Component
17815 * Bootstrap Progress class
17816 * @cfg {Boolean} striped striped of the progress bar
17817 * @cfg {Boolean} active animated of the progress bar
17821 * Create a new Progress
17822 * @param {Object} config The config object
17825 Roo.bootstrap.Progress = function(config){
17826 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17829 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
17834 getAutoCreate : function(){
17842 cfg.cls += ' progress-striped';
17846 cfg.cls += ' active';
17865 * @class Roo.bootstrap.ProgressBar
17866 * @extends Roo.bootstrap.Component
17867 * Bootstrap ProgressBar class
17868 * @cfg {Number} aria_valuenow aria-value now
17869 * @cfg {Number} aria_valuemin aria-value min
17870 * @cfg {Number} aria_valuemax aria-value max
17871 * @cfg {String} label label for the progress bar
17872 * @cfg {String} panel (success | info | warning | danger )
17873 * @cfg {String} role role of the progress bar
17874 * @cfg {String} sr_only text
17878 * Create a new ProgressBar
17879 * @param {Object} config The config object
17882 Roo.bootstrap.ProgressBar = function(config){
17883 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17886 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
17890 aria_valuemax : 100,
17896 getAutoCreate : function()
17901 cls: 'progress-bar',
17902 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17914 cfg.role = this.role;
17917 if(this.aria_valuenow){
17918 cfg['aria-valuenow'] = this.aria_valuenow;
17921 if(this.aria_valuemin){
17922 cfg['aria-valuemin'] = this.aria_valuemin;
17925 if(this.aria_valuemax){
17926 cfg['aria-valuemax'] = this.aria_valuemax;
17929 if(this.label && !this.sr_only){
17930 cfg.html = this.label;
17934 cfg.cls += ' progress-bar-' + this.panel;
17940 update : function(aria_valuenow)
17942 this.aria_valuenow = aria_valuenow;
17944 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17959 * @class Roo.bootstrap.TabGroup
17960 * @extends Roo.bootstrap.Column
17961 * Bootstrap Column class
17962 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17963 * @cfg {Boolean} carousel true to make the group behave like a carousel
17964 * @cfg {Boolean} bullets show bullets for the panels
17965 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17966 * @cfg {Number} timer auto slide timer .. default 0 millisecond
17967 * @cfg {Boolean} showarrow (true|false) show arrow default true
17970 * Create a new TabGroup
17971 * @param {Object} config The config object
17974 Roo.bootstrap.TabGroup = function(config){
17975 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17977 this.navId = Roo.id();
17980 Roo.bootstrap.TabGroup.register(this);
17984 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
17987 transition : false,
17992 slideOnTouch : false,
17995 getAutoCreate : function()
17997 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17999 cfg.cls += ' tab-content';
18001 if (this.carousel) {
18002 cfg.cls += ' carousel slide';
18005 cls : 'carousel-inner',
18009 if(this.bullets && !Roo.isTouch){
18012 cls : 'carousel-bullets',
18016 if(this.bullets_cls){
18017 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18024 cfg.cn[0].cn.push(bullets);
18027 if(this.showarrow){
18028 cfg.cn[0].cn.push({
18030 class : 'carousel-arrow',
18034 class : 'carousel-prev',
18038 class : 'fa fa-chevron-left'
18044 class : 'carousel-next',
18048 class : 'fa fa-chevron-right'
18061 initEvents: function()
18063 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18064 // this.el.on("touchstart", this.onTouchStart, this);
18067 if(this.autoslide){
18070 this.slideFn = window.setInterval(function() {
18071 _this.showPanelNext();
18075 if(this.showarrow){
18076 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18077 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18083 // onTouchStart : function(e, el, o)
18085 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18089 // this.showPanelNext();
18093 getChildContainer : function()
18095 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18099 * register a Navigation item
18100 * @param {Roo.bootstrap.NavItem} the navitem to add
18102 register : function(item)
18104 this.tabs.push( item);
18105 item.navId = this.navId; // not really needed..
18110 getActivePanel : function()
18113 Roo.each(this.tabs, function(t) {
18123 getPanelByName : function(n)
18126 Roo.each(this.tabs, function(t) {
18127 if (t.tabId == n) {
18135 indexOfPanel : function(p)
18138 Roo.each(this.tabs, function(t,i) {
18139 if (t.tabId == p.tabId) {
18148 * show a specific panel
18149 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18150 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18152 showPanel : function (pan)
18154 if(this.transition || typeof(pan) == 'undefined'){
18155 Roo.log("waiting for the transitionend");
18159 if (typeof(pan) == 'number') {
18160 pan = this.tabs[pan];
18163 if (typeof(pan) == 'string') {
18164 pan = this.getPanelByName(pan);
18167 var cur = this.getActivePanel();
18170 Roo.log('pan or acitve pan is undefined');
18174 if (pan.tabId == this.getActivePanel().tabId) {
18178 if (false === cur.fireEvent('beforedeactivate')) {
18182 if(this.bullets > 0 && !Roo.isTouch){
18183 this.setActiveBullet(this.indexOfPanel(pan));
18186 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18188 this.transition = true;
18189 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
18190 var lr = dir == 'next' ? 'left' : 'right';
18191 pan.el.addClass(dir); // or prev
18192 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18193 cur.el.addClass(lr); // or right
18194 pan.el.addClass(lr);
18197 cur.el.on('transitionend', function() {
18198 Roo.log("trans end?");
18200 pan.el.removeClass([lr,dir]);
18201 pan.setActive(true);
18203 cur.el.removeClass([lr]);
18204 cur.setActive(false);
18206 _this.transition = false;
18208 }, this, { single: true } );
18213 cur.setActive(false);
18214 pan.setActive(true);
18219 showPanelNext : function()
18221 var i = this.indexOfPanel(this.getActivePanel());
18223 if (i >= this.tabs.length - 1 && !this.autoslide) {
18227 if (i >= this.tabs.length - 1 && this.autoslide) {
18231 this.showPanel(this.tabs[i+1]);
18234 showPanelPrev : function()
18236 var i = this.indexOfPanel(this.getActivePanel());
18238 if (i < 1 && !this.autoslide) {
18242 if (i < 1 && this.autoslide) {
18243 i = this.tabs.length;
18246 this.showPanel(this.tabs[i-1]);
18250 addBullet: function()
18252 if(!this.bullets || Roo.isTouch){
18255 var ctr = this.el.select('.carousel-bullets',true).first();
18256 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18257 var bullet = ctr.createChild({
18258 cls : 'bullet bullet-' + i
18259 },ctr.dom.lastChild);
18264 bullet.on('click', (function(e, el, o, ii, t){
18266 e.preventDefault();
18268 this.showPanel(ii);
18270 if(this.autoslide && this.slideFn){
18271 clearInterval(this.slideFn);
18272 this.slideFn = window.setInterval(function() {
18273 _this.showPanelNext();
18277 }).createDelegate(this, [i, bullet], true));
18282 setActiveBullet : function(i)
18288 Roo.each(this.el.select('.bullet', true).elements, function(el){
18289 el.removeClass('selected');
18292 var bullet = this.el.select('.bullet-' + i, true).first();
18298 bullet.addClass('selected');
18309 Roo.apply(Roo.bootstrap.TabGroup, {
18313 * register a Navigation Group
18314 * @param {Roo.bootstrap.NavGroup} the navgroup to add
18316 register : function(navgrp)
18318 this.groups[navgrp.navId] = navgrp;
18322 * fetch a Navigation Group based on the navigation ID
18323 * if one does not exist , it will get created.
18324 * @param {string} the navgroup to add
18325 * @returns {Roo.bootstrap.NavGroup} the navgroup
18327 get: function(navId) {
18328 if (typeof(this.groups[navId]) == 'undefined') {
18329 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18331 return this.groups[navId] ;
18346 * @class Roo.bootstrap.TabPanel
18347 * @extends Roo.bootstrap.Component
18348 * Bootstrap TabPanel class
18349 * @cfg {Boolean} active panel active
18350 * @cfg {String} html panel content
18351 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18352 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18353 * @cfg {String} href click to link..
18357 * Create a new TabPanel
18358 * @param {Object} config The config object
18361 Roo.bootstrap.TabPanel = function(config){
18362 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18366 * Fires when the active status changes
18367 * @param {Roo.bootstrap.TabPanel} this
18368 * @param {Boolean} state the new state
18373 * @event beforedeactivate
18374 * Fires before a tab is de-activated - can be used to do validation on a form.
18375 * @param {Roo.bootstrap.TabPanel} this
18376 * @return {Boolean} false if there is an error
18379 'beforedeactivate': true
18382 this.tabId = this.tabId || Roo.id();
18386 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
18394 getAutoCreate : function(){
18397 // item is needed for carousel - not sure if it has any effect otherwise
18398 cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18399 html: this.html || ''
18403 cfg.cls += ' active';
18407 cfg.tabId = this.tabId;
18414 initEvents: function()
18416 var p = this.parent();
18418 this.navId = this.navId || p.navId;
18420 if (typeof(this.navId) != 'undefined') {
18421 // not really needed.. but just in case.. parent should be a NavGroup.
18422 var tg = Roo.bootstrap.TabGroup.get(this.navId);
18426 var i = tg.tabs.length - 1;
18428 if(this.active && tg.bullets > 0 && i < tg.bullets){
18429 tg.setActiveBullet(i);
18433 this.el.on('click', this.onClick, this);
18436 this.el.on("touchstart", this.onTouchStart, this);
18437 this.el.on("touchmove", this.onTouchMove, this);
18438 this.el.on("touchend", this.onTouchEnd, this);
18443 onRender : function(ct, position)
18445 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18448 setActive : function(state)
18450 Roo.log("panel - set active " + this.tabId + "=" + state);
18452 this.active = state;
18454 this.el.removeClass('active');
18456 } else if (!this.el.hasClass('active')) {
18457 this.el.addClass('active');
18460 this.fireEvent('changed', this, state);
18463 onClick : function(e)
18465 e.preventDefault();
18467 if(!this.href.length){
18471 window.location.href = this.href;
18480 onTouchStart : function(e)
18482 this.swiping = false;
18484 this.startX = e.browserEvent.touches[0].clientX;
18485 this.startY = e.browserEvent.touches[0].clientY;
18488 onTouchMove : function(e)
18490 this.swiping = true;
18492 this.endX = e.browserEvent.touches[0].clientX;
18493 this.endY = e.browserEvent.touches[0].clientY;
18496 onTouchEnd : function(e)
18503 var tabGroup = this.parent();
18505 if(this.endX > this.startX){ // swiping right
18506 tabGroup.showPanelPrev();
18510 if(this.startX > this.endX){ // swiping left
18511 tabGroup.showPanelNext();
18530 * @class Roo.bootstrap.DateField
18531 * @extends Roo.bootstrap.Input
18532 * Bootstrap DateField class
18533 * @cfg {Number} weekStart default 0
18534 * @cfg {String} viewMode default empty, (months|years)
18535 * @cfg {String} minViewMode default empty, (months|years)
18536 * @cfg {Number} startDate default -Infinity
18537 * @cfg {Number} endDate default Infinity
18538 * @cfg {Boolean} todayHighlight default false
18539 * @cfg {Boolean} todayBtn default false
18540 * @cfg {Boolean} calendarWeeks default false
18541 * @cfg {Object} daysOfWeekDisabled default empty
18542 * @cfg {Boolean} singleMode default false (true | false)
18544 * @cfg {Boolean} keyboardNavigation default true
18545 * @cfg {String} language default en
18548 * Create a new DateField
18549 * @param {Object} config The config object
18552 Roo.bootstrap.DateField = function(config){
18553 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18557 * Fires when this field show.
18558 * @param {Roo.bootstrap.DateField} this
18559 * @param {Mixed} date The date value
18564 * Fires when this field hide.
18565 * @param {Roo.bootstrap.DateField} this
18566 * @param {Mixed} date The date value
18571 * Fires when select a date.
18572 * @param {Roo.bootstrap.DateField} this
18573 * @param {Mixed} date The date value
18577 * @event beforeselect
18578 * Fires when before select a date.
18579 * @param {Roo.bootstrap.DateField} this
18580 * @param {Mixed} date The date value
18582 beforeselect : true
18586 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
18589 * @cfg {String} format
18590 * The default date format string which can be overriden for localization support. The format must be
18591 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18595 * @cfg {String} altFormats
18596 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18597 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18599 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18607 todayHighlight : false,
18613 keyboardNavigation: true,
18615 calendarWeeks: false,
18617 startDate: -Infinity,
18621 daysOfWeekDisabled: [],
18625 singleMode : false,
18627 UTCDate: function()
18629 return new Date(Date.UTC.apply(Date, arguments));
18632 UTCToday: function()
18634 var today = new Date();
18635 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18638 getDate: function() {
18639 var d = this.getUTCDate();
18640 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18643 getUTCDate: function() {
18647 setDate: function(d) {
18648 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18651 setUTCDate: function(d) {
18653 this.setValue(this.formatDate(this.date));
18656 onRender: function(ct, position)
18659 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18661 this.language = this.language || 'en';
18662 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18663 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18665 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18666 this.format = this.format || 'm/d/y';
18667 this.isInline = false;
18668 this.isInput = true;
18669 this.component = this.el.select('.add-on', true).first() || false;
18670 this.component = (this.component && this.component.length === 0) ? false : this.component;
18671 this.hasInput = this.component && this.inputEl().length;
18673 if (typeof(this.minViewMode === 'string')) {
18674 switch (this.minViewMode) {
18676 this.minViewMode = 1;
18679 this.minViewMode = 2;
18682 this.minViewMode = 0;
18687 if (typeof(this.viewMode === 'string')) {
18688 switch (this.viewMode) {
18701 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18703 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18705 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18707 this.picker().on('mousedown', this.onMousedown, this);
18708 this.picker().on('click', this.onClick, this);
18710 this.picker().addClass('datepicker-dropdown');
18712 this.startViewMode = this.viewMode;
18714 if(this.singleMode){
18715 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18716 v.setVisibilityMode(Roo.Element.DISPLAY);
18720 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18721 v.setStyle('width', '189px');
18725 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18726 if(!this.calendarWeeks){
18731 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18732 v.attr('colspan', function(i, val){
18733 return parseInt(val) + 1;
18738 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18740 this.setStartDate(this.startDate);
18741 this.setEndDate(this.endDate);
18743 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18750 if(this.isInline) {
18755 picker : function()
18757 return this.pickerEl;
18758 // return this.el.select('.datepicker', true).first();
18761 fillDow: function()
18763 var dowCnt = this.weekStart;
18772 if(this.calendarWeeks){
18780 while (dowCnt < this.weekStart + 7) {
18784 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18788 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18791 fillMonths: function()
18794 var months = this.picker().select('>.datepicker-months td', true).first();
18796 months.dom.innerHTML = '';
18802 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18805 months.createChild(month);
18812 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;
18814 if (this.date < this.startDate) {
18815 this.viewDate = new Date(this.startDate);
18816 } else if (this.date > this.endDate) {
18817 this.viewDate = new Date(this.endDate);
18819 this.viewDate = new Date(this.date);
18827 var d = new Date(this.viewDate),
18828 year = d.getUTCFullYear(),
18829 month = d.getUTCMonth(),
18830 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18831 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18832 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18833 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18834 currentDate = this.date && this.date.valueOf(),
18835 today = this.UTCToday();
18837 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18839 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18841 // this.picker.select('>tfoot th.today').
18842 // .text(dates[this.language].today)
18843 // .toggle(this.todayBtn !== false);
18845 this.updateNavArrows();
18848 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18850 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18852 prevMonth.setUTCDate(day);
18854 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18856 var nextMonth = new Date(prevMonth);
18858 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18860 nextMonth = nextMonth.valueOf();
18862 var fillMonths = false;
18864 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18866 while(prevMonth.valueOf() <= nextMonth) {
18869 if (prevMonth.getUTCDay() === this.weekStart) {
18871 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18879 if(this.calendarWeeks){
18880 // ISO 8601: First week contains first thursday.
18881 // ISO also states week starts on Monday, but we can be more abstract here.
18883 // Start of current week: based on weekstart/current date
18884 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18885 // Thursday of this week
18886 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18887 // First Thursday of year, year from thursday
18888 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18889 // Calendar week: ms between thursdays, div ms per day, div 7 days
18890 calWeek = (th - yth) / 864e5 / 7 + 1;
18892 fillMonths.cn.push({
18900 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18902 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18905 if (this.todayHighlight &&
18906 prevMonth.getUTCFullYear() == today.getFullYear() &&
18907 prevMonth.getUTCMonth() == today.getMonth() &&
18908 prevMonth.getUTCDate() == today.getDate()) {
18909 clsName += ' today';
18912 if (currentDate && prevMonth.valueOf() === currentDate) {
18913 clsName += ' active';
18916 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18917 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18918 clsName += ' disabled';
18921 fillMonths.cn.push({
18923 cls: 'day ' + clsName,
18924 html: prevMonth.getDate()
18927 prevMonth.setDate(prevMonth.getDate()+1);
18930 var currentYear = this.date && this.date.getUTCFullYear();
18931 var currentMonth = this.date && this.date.getUTCMonth();
18933 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18935 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18936 v.removeClass('active');
18938 if(currentYear === year && k === currentMonth){
18939 v.addClass('active');
18942 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18943 v.addClass('disabled');
18949 year = parseInt(year/10, 10) * 10;
18951 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18953 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18956 for (var i = -1; i < 11; i++) {
18957 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18959 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18967 showMode: function(dir)
18970 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18973 Roo.each(this.picker().select('>div',true).elements, function(v){
18974 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18977 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18982 if(this.isInline) {
18986 this.picker().removeClass(['bottom', 'top']);
18988 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18990 * place to the top of element!
18994 this.picker().addClass('top');
18995 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19000 this.picker().addClass('bottom');
19002 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19005 parseDate : function(value)
19007 if(!value || value instanceof Date){
19010 var v = Date.parseDate(value, this.format);
19011 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19012 v = Date.parseDate(value, 'Y-m-d');
19014 if(!v && this.altFormats){
19015 if(!this.altFormatsArray){
19016 this.altFormatsArray = this.altFormats.split("|");
19018 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19019 v = Date.parseDate(value, this.altFormatsArray[i]);
19025 formatDate : function(date, fmt)
19027 return (!date || !(date instanceof Date)) ?
19028 date : date.dateFormat(fmt || this.format);
19031 onFocus : function()
19033 Roo.bootstrap.DateField.superclass.onFocus.call(this);
19037 onBlur : function()
19039 Roo.bootstrap.DateField.superclass.onBlur.call(this);
19041 var d = this.inputEl().getValue();
19048 showPopup : function()
19050 this.picker().show();
19054 this.fireEvent('showpopup', this, this.date);
19057 hidePopup : function()
19059 if(this.isInline) {
19062 this.picker().hide();
19063 this.viewMode = this.startViewMode;
19066 this.fireEvent('hidepopup', this, this.date);
19070 onMousedown: function(e)
19072 e.stopPropagation();
19073 e.preventDefault();
19078 Roo.bootstrap.DateField.superclass.keyup.call(this);
19082 setValue: function(v)
19084 if(this.fireEvent('beforeselect', this, v) !== false){
19085 var d = new Date(this.parseDate(v) ).clearTime();
19087 if(isNaN(d.getTime())){
19088 this.date = this.viewDate = '';
19089 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19093 v = this.formatDate(d);
19095 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19097 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19101 this.fireEvent('select', this, this.date);
19105 getValue: function()
19107 return this.formatDate(this.date);
19110 fireKey: function(e)
19112 if (!this.picker().isVisible()){
19113 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19119 var dateChanged = false,
19121 newDate, newViewDate;
19126 e.preventDefault();
19130 if (!this.keyboardNavigation) {
19133 dir = e.keyCode == 37 ? -1 : 1;
19136 newDate = this.moveYear(this.date, dir);
19137 newViewDate = this.moveYear(this.viewDate, dir);
19138 } else if (e.shiftKey){
19139 newDate = this.moveMonth(this.date, dir);
19140 newViewDate = this.moveMonth(this.viewDate, dir);
19142 newDate = new Date(this.date);
19143 newDate.setUTCDate(this.date.getUTCDate() + dir);
19144 newViewDate = new Date(this.viewDate);
19145 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19147 if (this.dateWithinRange(newDate)){
19148 this.date = newDate;
19149 this.viewDate = newViewDate;
19150 this.setValue(this.formatDate(this.date));
19152 e.preventDefault();
19153 dateChanged = true;
19158 if (!this.keyboardNavigation) {
19161 dir = e.keyCode == 38 ? -1 : 1;
19163 newDate = this.moveYear(this.date, dir);
19164 newViewDate = this.moveYear(this.viewDate, dir);
19165 } else if (e.shiftKey){
19166 newDate = this.moveMonth(this.date, dir);
19167 newViewDate = this.moveMonth(this.viewDate, dir);
19169 newDate = new Date(this.date);
19170 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19171 newViewDate = new Date(this.viewDate);
19172 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19174 if (this.dateWithinRange(newDate)){
19175 this.date = newDate;
19176 this.viewDate = newViewDate;
19177 this.setValue(this.formatDate(this.date));
19179 e.preventDefault();
19180 dateChanged = true;
19184 this.setValue(this.formatDate(this.date));
19186 e.preventDefault();
19189 this.setValue(this.formatDate(this.date));
19203 onClick: function(e)
19205 e.stopPropagation();
19206 e.preventDefault();
19208 var target = e.getTarget();
19210 if(target.nodeName.toLowerCase() === 'i'){
19211 target = Roo.get(target).dom.parentNode;
19214 var nodeName = target.nodeName;
19215 var className = target.className;
19216 var html = target.innerHTML;
19217 //Roo.log(nodeName);
19219 switch(nodeName.toLowerCase()) {
19221 switch(className) {
19227 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19228 switch(this.viewMode){
19230 this.viewDate = this.moveMonth(this.viewDate, dir);
19234 this.viewDate = this.moveYear(this.viewDate, dir);
19240 var date = new Date();
19241 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19243 this.setValue(this.formatDate(this.date));
19250 if (className.indexOf('disabled') < 0) {
19251 this.viewDate.setUTCDate(1);
19252 if (className.indexOf('month') > -1) {
19253 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19255 var year = parseInt(html, 10) || 0;
19256 this.viewDate.setUTCFullYear(year);
19260 if(this.singleMode){
19261 this.setValue(this.formatDate(this.viewDate));
19272 //Roo.log(className);
19273 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19274 var day = parseInt(html, 10) || 1;
19275 var year = this.viewDate.getUTCFullYear(),
19276 month = this.viewDate.getUTCMonth();
19278 if (className.indexOf('old') > -1) {
19285 } else if (className.indexOf('new') > -1) {
19293 //Roo.log([year,month,day]);
19294 this.date = this.UTCDate(year, month, day,0,0,0,0);
19295 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19297 //Roo.log(this.formatDate(this.date));
19298 this.setValue(this.formatDate(this.date));
19305 setStartDate: function(startDate)
19307 this.startDate = startDate || -Infinity;
19308 if (this.startDate !== -Infinity) {
19309 this.startDate = this.parseDate(this.startDate);
19312 this.updateNavArrows();
19315 setEndDate: function(endDate)
19317 this.endDate = endDate || Infinity;
19318 if (this.endDate !== Infinity) {
19319 this.endDate = this.parseDate(this.endDate);
19322 this.updateNavArrows();
19325 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19327 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19328 if (typeof(this.daysOfWeekDisabled) !== 'object') {
19329 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19331 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19332 return parseInt(d, 10);
19335 this.updateNavArrows();
19338 updateNavArrows: function()
19340 if(this.singleMode){
19344 var d = new Date(this.viewDate),
19345 year = d.getUTCFullYear(),
19346 month = d.getUTCMonth();
19348 Roo.each(this.picker().select('.prev', true).elements, function(v){
19350 switch (this.viewMode) {
19353 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19359 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19366 Roo.each(this.picker().select('.next', true).elements, function(v){
19368 switch (this.viewMode) {
19371 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19377 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19385 moveMonth: function(date, dir)
19390 var new_date = new Date(date.valueOf()),
19391 day = new_date.getUTCDate(),
19392 month = new_date.getUTCMonth(),
19393 mag = Math.abs(dir),
19395 dir = dir > 0 ? 1 : -1;
19398 // If going back one month, make sure month is not current month
19399 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19401 return new_date.getUTCMonth() == month;
19403 // If going forward one month, make sure month is as expected
19404 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19406 return new_date.getUTCMonth() != new_month;
19408 new_month = month + dir;
19409 new_date.setUTCMonth(new_month);
19410 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19411 if (new_month < 0 || new_month > 11) {
19412 new_month = (new_month + 12) % 12;
19415 // For magnitudes >1, move one month at a time...
19416 for (var i=0; i<mag; i++) {
19417 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19418 new_date = this.moveMonth(new_date, dir);
19420 // ...then reset the day, keeping it in the new month
19421 new_month = new_date.getUTCMonth();
19422 new_date.setUTCDate(day);
19424 return new_month != new_date.getUTCMonth();
19427 // Common date-resetting loop -- if date is beyond end of month, make it
19430 new_date.setUTCDate(--day);
19431 new_date.setUTCMonth(new_month);
19436 moveYear: function(date, dir)
19438 return this.moveMonth(date, dir*12);
19441 dateWithinRange: function(date)
19443 return date >= this.startDate && date <= this.endDate;
19449 this.picker().remove();
19452 validateValue : function(value)
19454 if(this.getVisibilityEl().hasClass('hidden')){
19458 if(value.length < 1) {
19459 if(this.allowBlank){
19465 if(value.length < this.minLength){
19468 if(value.length > this.maxLength){
19472 var vt = Roo.form.VTypes;
19473 if(!vt[this.vtype](value, this)){
19477 if(typeof this.validator == "function"){
19478 var msg = this.validator(value);
19484 if(this.regex && !this.regex.test(value)){
19488 if(typeof(this.parseDate(value)) == 'undefined'){
19492 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19496 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19506 this.date = this.viewDate = '';
19508 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19513 Roo.apply(Roo.bootstrap.DateField, {
19524 html: '<i class="fa fa-arrow-left"/>'
19534 html: '<i class="fa fa-arrow-right"/>'
19576 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19577 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19578 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19579 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19580 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19593 navFnc: 'FullYear',
19598 navFnc: 'FullYear',
19603 Roo.apply(Roo.bootstrap.DateField, {
19607 cls: 'datepicker dropdown-menu roo-dynamic',
19611 cls: 'datepicker-days',
19615 cls: 'table-condensed',
19617 Roo.bootstrap.DateField.head,
19621 Roo.bootstrap.DateField.footer
19628 cls: 'datepicker-months',
19632 cls: 'table-condensed',
19634 Roo.bootstrap.DateField.head,
19635 Roo.bootstrap.DateField.content,
19636 Roo.bootstrap.DateField.footer
19643 cls: 'datepicker-years',
19647 cls: 'table-condensed',
19649 Roo.bootstrap.DateField.head,
19650 Roo.bootstrap.DateField.content,
19651 Roo.bootstrap.DateField.footer
19670 * @class Roo.bootstrap.TimeField
19671 * @extends Roo.bootstrap.Input
19672 * Bootstrap DateField class
19676 * Create a new TimeField
19677 * @param {Object} config The config object
19680 Roo.bootstrap.TimeField = function(config){
19681 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19685 * Fires when this field show.
19686 * @param {Roo.bootstrap.DateField} thisthis
19687 * @param {Mixed} date The date value
19692 * Fires when this field hide.
19693 * @param {Roo.bootstrap.DateField} this
19694 * @param {Mixed} date The date value
19699 * Fires when select a date.
19700 * @param {Roo.bootstrap.DateField} this
19701 * @param {Mixed} date The date value
19707 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
19710 * @cfg {String} format
19711 * The default time format string which can be overriden for localization support. The format must be
19712 * valid according to {@link Date#parseDate} (defaults to 'H:i').
19716 onRender: function(ct, position)
19719 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19721 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19723 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19725 this.pop = this.picker().select('>.datepicker-time',true).first();
19726 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19728 this.picker().on('mousedown', this.onMousedown, this);
19729 this.picker().on('click', this.onClick, this);
19731 this.picker().addClass('datepicker-dropdown');
19736 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19737 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19738 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19739 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19740 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19741 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19745 fireKey: function(e){
19746 if (!this.picker().isVisible()){
19747 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19753 e.preventDefault();
19761 this.onTogglePeriod();
19764 this.onIncrementMinutes();
19767 this.onDecrementMinutes();
19776 onClick: function(e) {
19777 e.stopPropagation();
19778 e.preventDefault();
19781 picker : function()
19783 return this.el.select('.datepicker', true).first();
19786 fillTime: function()
19788 var time = this.pop.select('tbody', true).first();
19790 time.dom.innerHTML = '';
19805 cls: 'hours-up glyphicon glyphicon-chevron-up'
19825 cls: 'minutes-up glyphicon glyphicon-chevron-up'
19846 cls: 'timepicker-hour',
19861 cls: 'timepicker-minute',
19876 cls: 'btn btn-primary period',
19898 cls: 'hours-down glyphicon glyphicon-chevron-down'
19918 cls: 'minutes-down glyphicon glyphicon-chevron-down'
19936 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19943 var hours = this.time.getHours();
19944 var minutes = this.time.getMinutes();
19957 hours = hours - 12;
19961 hours = '0' + hours;
19965 minutes = '0' + minutes;
19968 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19969 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19970 this.pop.select('button', true).first().dom.innerHTML = period;
19976 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19978 var cls = ['bottom'];
19980 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19987 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19992 this.picker().addClass(cls.join('-'));
19996 Roo.each(cls, function(c){
19998 _this.picker().setTop(_this.inputEl().getHeight());
20002 _this.picker().setTop(0 - _this.picker().getHeight());
20007 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20011 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20018 onFocus : function()
20020 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20024 onBlur : function()
20026 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20032 this.picker().show();
20037 this.fireEvent('show', this, this.date);
20042 this.picker().hide();
20045 this.fireEvent('hide', this, this.date);
20048 setTime : function()
20051 this.setValue(this.time.format(this.format));
20053 this.fireEvent('select', this, this.date);
20058 onMousedown: function(e){
20059 e.stopPropagation();
20060 e.preventDefault();
20063 onIncrementHours: function()
20065 Roo.log('onIncrementHours');
20066 this.time = this.time.add(Date.HOUR, 1);
20071 onDecrementHours: function()
20073 Roo.log('onDecrementHours');
20074 this.time = this.time.add(Date.HOUR, -1);
20078 onIncrementMinutes: function()
20080 Roo.log('onIncrementMinutes');
20081 this.time = this.time.add(Date.MINUTE, 1);
20085 onDecrementMinutes: function()
20087 Roo.log('onDecrementMinutes');
20088 this.time = this.time.add(Date.MINUTE, -1);
20092 onTogglePeriod: function()
20094 Roo.log('onTogglePeriod');
20095 this.time = this.time.add(Date.HOUR, 12);
20102 Roo.apply(Roo.bootstrap.TimeField, {
20132 cls: 'btn btn-info ok',
20144 Roo.apply(Roo.bootstrap.TimeField, {
20148 cls: 'datepicker dropdown-menu',
20152 cls: 'datepicker-time',
20156 cls: 'table-condensed',
20158 Roo.bootstrap.TimeField.content,
20159 Roo.bootstrap.TimeField.footer
20178 * @class Roo.bootstrap.MonthField
20179 * @extends Roo.bootstrap.Input
20180 * Bootstrap MonthField class
20182 * @cfg {String} language default en
20185 * Create a new MonthField
20186 * @param {Object} config The config object
20189 Roo.bootstrap.MonthField = function(config){
20190 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20195 * Fires when this field show.
20196 * @param {Roo.bootstrap.MonthField} this
20197 * @param {Mixed} date The date value
20202 * Fires when this field hide.
20203 * @param {Roo.bootstrap.MonthField} this
20204 * @param {Mixed} date The date value
20209 * Fires when select a date.
20210 * @param {Roo.bootstrap.MonthField} this
20211 * @param {String} oldvalue The old value
20212 * @param {String} newvalue The new value
20218 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
20220 onRender: function(ct, position)
20223 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20225 this.language = this.language || 'en';
20226 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20227 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20229 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20230 this.isInline = false;
20231 this.isInput = true;
20232 this.component = this.el.select('.add-on', true).first() || false;
20233 this.component = (this.component && this.component.length === 0) ? false : this.component;
20234 this.hasInput = this.component && this.inputEL().length;
20236 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20238 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20240 this.picker().on('mousedown', this.onMousedown, this);
20241 this.picker().on('click', this.onClick, this);
20243 this.picker().addClass('datepicker-dropdown');
20245 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20246 v.setStyle('width', '189px');
20253 if(this.isInline) {
20259 setValue: function(v, suppressEvent)
20261 var o = this.getValue();
20263 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20267 if(suppressEvent !== true){
20268 this.fireEvent('select', this, o, v);
20273 getValue: function()
20278 onClick: function(e)
20280 e.stopPropagation();
20281 e.preventDefault();
20283 var target = e.getTarget();
20285 if(target.nodeName.toLowerCase() === 'i'){
20286 target = Roo.get(target).dom.parentNode;
20289 var nodeName = target.nodeName;
20290 var className = target.className;
20291 var html = target.innerHTML;
20293 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20297 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20299 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20305 picker : function()
20307 return this.pickerEl;
20310 fillMonths: function()
20313 var months = this.picker().select('>.datepicker-months td', true).first();
20315 months.dom.innerHTML = '';
20321 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20324 months.createChild(month);
20333 if(typeof(this.vIndex) == 'undefined' && this.value.length){
20334 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20337 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20338 e.removeClass('active');
20340 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20341 e.addClass('active');
20348 if(this.isInline) {
20352 this.picker().removeClass(['bottom', 'top']);
20354 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20356 * place to the top of element!
20360 this.picker().addClass('top');
20361 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20366 this.picker().addClass('bottom');
20368 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20371 onFocus : function()
20373 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20377 onBlur : function()
20379 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20381 var d = this.inputEl().getValue();
20390 this.picker().show();
20391 this.picker().select('>.datepicker-months', true).first().show();
20395 this.fireEvent('show', this, this.date);
20400 if(this.isInline) {
20403 this.picker().hide();
20404 this.fireEvent('hide', this, this.date);
20408 onMousedown: function(e)
20410 e.stopPropagation();
20411 e.preventDefault();
20416 Roo.bootstrap.MonthField.superclass.keyup.call(this);
20420 fireKey: function(e)
20422 if (!this.picker().isVisible()){
20423 if (e.keyCode == 27) {// allow escape to hide and re-show picker
20434 e.preventDefault();
20438 dir = e.keyCode == 37 ? -1 : 1;
20440 this.vIndex = this.vIndex + dir;
20442 if(this.vIndex < 0){
20446 if(this.vIndex > 11){
20450 if(isNaN(this.vIndex)){
20454 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20460 dir = e.keyCode == 38 ? -1 : 1;
20462 this.vIndex = this.vIndex + dir * 4;
20464 if(this.vIndex < 0){
20468 if(this.vIndex > 11){
20472 if(isNaN(this.vIndex)){
20476 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20481 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20482 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20486 e.preventDefault();
20489 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20490 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20506 this.picker().remove();
20511 Roo.apply(Roo.bootstrap.MonthField, {
20530 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20531 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20536 Roo.apply(Roo.bootstrap.MonthField, {
20540 cls: 'datepicker dropdown-menu roo-dynamic',
20544 cls: 'datepicker-months',
20548 cls: 'table-condensed',
20550 Roo.bootstrap.DateField.content
20570 * @class Roo.bootstrap.CheckBox
20571 * @extends Roo.bootstrap.Input
20572 * Bootstrap CheckBox class
20574 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20575 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20576 * @cfg {String} boxLabel The text that appears beside the checkbox
20577 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20578 * @cfg {Boolean} checked initnal the element
20579 * @cfg {Boolean} inline inline the element (default false)
20580 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20581 * @cfg {String} tooltip label tooltip
20584 * Create a new CheckBox
20585 * @param {Object} config The config object
20588 Roo.bootstrap.CheckBox = function(config){
20589 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20594 * Fires when the element is checked or unchecked.
20595 * @param {Roo.bootstrap.CheckBox} this This input
20596 * @param {Boolean} checked The new checked value
20601 * Fires when the element is click.
20602 * @param {Roo.bootstrap.CheckBox} this This input
20609 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
20611 inputType: 'checkbox',
20620 getAutoCreate : function()
20622 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20628 cfg.cls = 'form-group ' + this.inputType; //input-group
20631 cfg.cls += ' ' + this.inputType + '-inline';
20637 type : this.inputType,
20638 value : this.inputValue,
20639 cls : 'roo-' + this.inputType, //'form-box',
20640 placeholder : this.placeholder || ''
20644 if(this.inputType != 'radio'){
20648 cls : 'roo-hidden-value',
20649 value : this.checked ? this.inputValue : this.valueOff
20654 if (this.weight) { // Validity check?
20655 cfg.cls += " " + this.inputType + "-" + this.weight;
20658 if (this.disabled) {
20659 input.disabled=true;
20663 input.checked = this.checked;
20668 input.name = this.name;
20670 if(this.inputType != 'radio'){
20671 hidden.name = this.name;
20672 input.name = '_hidden_' + this.name;
20677 input.cls += ' input-' + this.size;
20682 ['xs','sm','md','lg'].map(function(size){
20683 if (settings[size]) {
20684 cfg.cls += ' col-' + size + '-' + settings[size];
20688 var inputblock = input;
20690 if (this.before || this.after) {
20693 cls : 'input-group',
20698 inputblock.cn.push({
20700 cls : 'input-group-addon',
20705 inputblock.cn.push(input);
20707 if(this.inputType != 'radio'){
20708 inputblock.cn.push(hidden);
20712 inputblock.cn.push({
20714 cls : 'input-group-addon',
20721 if (align ==='left' && this.fieldLabel.length) {
20722 // Roo.log("left and has label");
20727 cls : 'control-label',
20728 html : this.fieldLabel
20738 if(this.labelWidth > 12){
20739 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20742 if(this.labelWidth < 13 && this.labelmd == 0){
20743 this.labelmd = this.labelWidth;
20746 if(this.labellg > 0){
20747 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20748 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20751 if(this.labelmd > 0){
20752 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20753 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20756 if(this.labelsm > 0){
20757 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20758 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20761 if(this.labelxs > 0){
20762 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20763 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20766 } else if ( this.fieldLabel.length) {
20767 // Roo.log(" label");
20771 tag: this.boxLabel ? 'span' : 'label',
20773 cls: 'control-label box-input-label',
20774 //cls : 'input-group-addon',
20775 html : this.fieldLabel
20784 // Roo.log(" no label && no align");
20785 cfg.cn = [ inputblock ] ;
20791 var boxLabelCfg = {
20793 //'for': id, // box label is handled by onclick - so no for...
20795 html: this.boxLabel
20799 boxLabelCfg.tooltip = this.tooltip;
20802 cfg.cn.push(boxLabelCfg);
20805 if(this.inputType != 'radio'){
20806 cfg.cn.push(hidden);
20814 * return the real input element.
20816 inputEl: function ()
20818 return this.el.select('input.roo-' + this.inputType,true).first();
20820 hiddenEl: function ()
20822 return this.el.select('input.roo-hidden-value',true).first();
20825 labelEl: function()
20827 return this.el.select('label.control-label',true).first();
20829 /* depricated... */
20833 return this.labelEl();
20836 boxLabelEl: function()
20838 return this.el.select('label.box-label',true).first();
20841 initEvents : function()
20843 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20845 this.inputEl().on('click', this.onClick, this);
20847 if (this.boxLabel) {
20848 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
20851 this.startValue = this.getValue();
20854 Roo.bootstrap.CheckBox.register(this);
20858 onClick : function(e)
20860 if(this.fireEvent('click', this, e) !== false){
20861 this.setChecked(!this.checked);
20866 setChecked : function(state,suppressEvent)
20868 this.startValue = this.getValue();
20870 if(this.inputType == 'radio'){
20872 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20873 e.dom.checked = false;
20876 this.inputEl().dom.checked = true;
20878 this.inputEl().dom.value = this.inputValue;
20880 if(suppressEvent !== true){
20881 this.fireEvent('check', this, true);
20889 this.checked = state;
20891 this.inputEl().dom.checked = state;
20894 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20896 if(suppressEvent !== true){
20897 this.fireEvent('check', this, state);
20903 getValue : function()
20905 if(this.inputType == 'radio'){
20906 return this.getGroupValue();
20909 return this.hiddenEl().dom.value;
20913 getGroupValue : function()
20915 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20919 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20922 setValue : function(v,suppressEvent)
20924 if(this.inputType == 'radio'){
20925 this.setGroupValue(v, suppressEvent);
20929 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20934 setGroupValue : function(v, suppressEvent)
20936 this.startValue = this.getValue();
20938 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20939 e.dom.checked = false;
20941 if(e.dom.value == v){
20942 e.dom.checked = true;
20946 if(suppressEvent !== true){
20947 this.fireEvent('check', this, true);
20955 validate : function()
20957 if(this.getVisibilityEl().hasClass('hidden')){
20963 (this.inputType == 'radio' && this.validateRadio()) ||
20964 (this.inputType == 'checkbox' && this.validateCheckbox())
20970 this.markInvalid();
20974 validateRadio : function()
20976 if(this.getVisibilityEl().hasClass('hidden')){
20980 if(this.allowBlank){
20986 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20987 if(!e.dom.checked){
20999 validateCheckbox : function()
21002 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21003 //return (this.getValue() == this.inputValue) ? true : false;
21006 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21014 for(var i in group){
21015 if(group[i].el.isVisible(true)){
21023 for(var i in group){
21028 r = (group[i].getValue() == group[i].inputValue) ? true : false;
21035 * Mark this field as valid
21037 markValid : function()
21041 this.fireEvent('valid', this);
21043 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21046 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21053 if(this.inputType == 'radio'){
21054 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21055 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21056 e.findParent('.form-group', false, true).addClass(_this.validClass);
21063 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21064 this.el.findParent('.form-group', false, true).addClass(this.validClass);
21068 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21074 for(var i in group){
21075 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21076 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
21081 * Mark this field as invalid
21082 * @param {String} msg The validation message
21084 markInvalid : function(msg)
21086 if(this.allowBlank){
21092 this.fireEvent('invalid', this, msg);
21094 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21097 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21101 label.markInvalid();
21104 if(this.inputType == 'radio'){
21105 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21106 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21107 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21114 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21115 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21119 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21125 for(var i in group){
21126 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21127 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21132 clearInvalid : function()
21134 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21136 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21138 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21140 if (label && label.iconEl) {
21141 label.iconEl.removeClass(label.validClass);
21142 label.iconEl.removeClass(label.invalidClass);
21146 disable : function()
21148 if(this.inputType != 'radio'){
21149 Roo.bootstrap.CheckBox.superclass.disable.call(this);
21156 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21157 _this.getActionEl().addClass(this.disabledClass);
21158 e.dom.disabled = true;
21162 this.disabled = true;
21163 this.fireEvent("disable", this);
21167 enable : function()
21169 if(this.inputType != 'radio'){
21170 Roo.bootstrap.CheckBox.superclass.enable.call(this);
21177 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21178 _this.getActionEl().removeClass(this.disabledClass);
21179 e.dom.disabled = false;
21183 this.disabled = false;
21184 this.fireEvent("enable", this);
21188 setBoxLabel : function(v)
21193 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21199 Roo.apply(Roo.bootstrap.CheckBox, {
21204 * register a CheckBox Group
21205 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21207 register : function(checkbox)
21209 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21210 this.groups[checkbox.groupId] = {};
21213 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21217 this.groups[checkbox.groupId][checkbox.name] = checkbox;
21221 * fetch a CheckBox Group based on the group ID
21222 * @param {string} the group ID
21223 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21225 get: function(groupId) {
21226 if (typeof(this.groups[groupId]) == 'undefined') {
21230 return this.groups[groupId] ;
21243 * @class Roo.bootstrap.Radio
21244 * @extends Roo.bootstrap.Component
21245 * Bootstrap Radio class
21246 * @cfg {String} boxLabel - the label associated
21247 * @cfg {String} value - the value of radio
21250 * Create a new Radio
21251 * @param {Object} config The config object
21253 Roo.bootstrap.Radio = function(config){
21254 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21258 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21264 getAutoCreate : function()
21268 cls : 'form-group radio',
21273 html : this.boxLabel
21281 initEvents : function()
21283 this.parent().register(this);
21285 this.el.on('click', this.onClick, this);
21289 onClick : function(e)
21291 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21292 this.setChecked(true);
21296 setChecked : function(state, suppressEvent)
21298 this.parent().setValue(this.value, suppressEvent);
21302 setBoxLabel : function(v)
21307 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21322 * @class Roo.bootstrap.SecurePass
21323 * @extends Roo.bootstrap.Input
21324 * Bootstrap SecurePass class
21328 * Create a new SecurePass
21329 * @param {Object} config The config object
21332 Roo.bootstrap.SecurePass = function (config) {
21333 // these go here, so the translation tool can replace them..
21335 PwdEmpty: "Please type a password, and then retype it to confirm.",
21336 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21337 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21338 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21339 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21340 FNInPwd: "Your password can't contain your first name. Please type a different password.",
21341 LNInPwd: "Your password can't contain your last name. Please type a different password.",
21342 TooWeak: "Your password is Too Weak."
21344 this.meterLabel = "Password strength:";
21345 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21346 this.meterClass = [
21347 "roo-password-meter-tooweak",
21348 "roo-password-meter-weak",
21349 "roo-password-meter-medium",
21350 "roo-password-meter-strong",
21351 "roo-password-meter-grey"
21356 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21359 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21361 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21363 * PwdEmpty: "Please type a password, and then retype it to confirm.",
21364 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21365 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21366 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21367 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21368 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
21369 * LNInPwd: "Your password can't contain your last name. Please type a different password."
21379 * @cfg {String/Object} Label for the strength meter (defaults to
21380 * 'Password strength:')
21385 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21386 * ['Weak', 'Medium', 'Strong'])
21389 pwdStrengths: false,
21402 initEvents: function ()
21404 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21406 if (this.el.is('input[type=password]') && Roo.isSafari) {
21407 this.el.on('keydown', this.SafariOnKeyDown, this);
21410 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21413 onRender: function (ct, position)
21415 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21416 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21417 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21419 this.trigger.createChild({
21424 cls: 'roo-password-meter-grey col-xs-12',
21427 //width: this.meterWidth + 'px'
21431 cls: 'roo-password-meter-text'
21437 if (this.hideTrigger) {
21438 this.trigger.setDisplayed(false);
21440 this.setSize(this.width || '', this.height || '');
21443 onDestroy: function ()
21445 if (this.trigger) {
21446 this.trigger.removeAllListeners();
21447 this.trigger.remove();
21450 this.wrap.remove();
21452 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21455 checkStrength: function ()
21457 var pwd = this.inputEl().getValue();
21458 if (pwd == this._lastPwd) {
21463 if (this.ClientSideStrongPassword(pwd)) {
21465 } else if (this.ClientSideMediumPassword(pwd)) {
21467 } else if (this.ClientSideWeakPassword(pwd)) {
21473 Roo.log('strength1: ' + strength);
21475 //var pm = this.trigger.child('div/div/div').dom;
21476 var pm = this.trigger.child('div/div');
21477 pm.removeClass(this.meterClass);
21478 pm.addClass(this.meterClass[strength]);
21481 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21483 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21485 this._lastPwd = pwd;
21489 Roo.bootstrap.SecurePass.superclass.reset.call(this);
21491 this._lastPwd = '';
21493 var pm = this.trigger.child('div/div');
21494 pm.removeClass(this.meterClass);
21495 pm.addClass('roo-password-meter-grey');
21498 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21501 this.inputEl().dom.type='password';
21504 validateValue: function (value)
21507 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21510 if (value.length == 0) {
21511 if (this.allowBlank) {
21512 this.clearInvalid();
21516 this.markInvalid(this.errors.PwdEmpty);
21517 this.errorMsg = this.errors.PwdEmpty;
21525 if ('[\x21-\x7e]*'.match(value)) {
21526 this.markInvalid(this.errors.PwdBadChar);
21527 this.errorMsg = this.errors.PwdBadChar;
21530 if (value.length < 6) {
21531 this.markInvalid(this.errors.PwdShort);
21532 this.errorMsg = this.errors.PwdShort;
21535 if (value.length > 16) {
21536 this.markInvalid(this.errors.PwdLong);
21537 this.errorMsg = this.errors.PwdLong;
21541 if (this.ClientSideStrongPassword(value)) {
21543 } else if (this.ClientSideMediumPassword(value)) {
21545 } else if (this.ClientSideWeakPassword(value)) {
21552 if (strength < 2) {
21553 //this.markInvalid(this.errors.TooWeak);
21554 this.errorMsg = this.errors.TooWeak;
21559 console.log('strength2: ' + strength);
21561 //var pm = this.trigger.child('div/div/div').dom;
21563 var pm = this.trigger.child('div/div');
21564 pm.removeClass(this.meterClass);
21565 pm.addClass(this.meterClass[strength]);
21567 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21569 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21571 this.errorMsg = '';
21575 CharacterSetChecks: function (type)
21578 this.fResult = false;
21581 isctype: function (character, type)
21584 case this.kCapitalLetter:
21585 if (character >= 'A' && character <= 'Z') {
21590 case this.kSmallLetter:
21591 if (character >= 'a' && character <= 'z') {
21597 if (character >= '0' && character <= '9') {
21602 case this.kPunctuation:
21603 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21614 IsLongEnough: function (pwd, size)
21616 return !(pwd == null || isNaN(size) || pwd.length < size);
21619 SpansEnoughCharacterSets: function (word, nb)
21621 if (!this.IsLongEnough(word, nb))
21626 var characterSetChecks = new Array(
21627 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21628 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21631 for (var index = 0; index < word.length; ++index) {
21632 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21633 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21634 characterSetChecks[nCharSet].fResult = true;
21641 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21642 if (characterSetChecks[nCharSet].fResult) {
21647 if (nCharSets < nb) {
21653 ClientSideStrongPassword: function (pwd)
21655 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21658 ClientSideMediumPassword: function (pwd)
21660 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21663 ClientSideWeakPassword: function (pwd)
21665 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21668 })//<script type="text/javascript">
21671 * Based Ext JS Library 1.1.1
21672 * Copyright(c) 2006-2007, Ext JS, LLC.
21678 * @class Roo.HtmlEditorCore
21679 * @extends Roo.Component
21680 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21682 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21685 Roo.HtmlEditorCore = function(config){
21688 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21693 * @event initialize
21694 * Fires when the editor is fully initialized (including the iframe)
21695 * @param {Roo.HtmlEditorCore} this
21700 * Fires when the editor is first receives the focus. Any insertion must wait
21701 * until after this event.
21702 * @param {Roo.HtmlEditorCore} this
21706 * @event beforesync
21707 * Fires before the textarea is updated with content from the editor iframe. Return false
21708 * to cancel the sync.
21709 * @param {Roo.HtmlEditorCore} this
21710 * @param {String} html
21714 * @event beforepush
21715 * Fires before the iframe editor is updated with content from the textarea. Return false
21716 * to cancel the push.
21717 * @param {Roo.HtmlEditorCore} this
21718 * @param {String} html
21723 * Fires when the textarea is updated with content from the editor iframe.
21724 * @param {Roo.HtmlEditorCore} this
21725 * @param {String} html
21730 * Fires when the iframe editor is updated with content from the textarea.
21731 * @param {Roo.HtmlEditorCore} this
21732 * @param {String} html
21737 * @event editorevent
21738 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21739 * @param {Roo.HtmlEditorCore} this
21745 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21747 // defaults : white / black...
21748 this.applyBlacklists();
21755 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
21759 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
21765 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21770 * @cfg {Number} height (in pixels)
21774 * @cfg {Number} width (in pixels)
21779 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21782 stylesheets: false,
21787 // private properties
21788 validationEvent : false,
21790 initialized : false,
21792 sourceEditMode : false,
21793 onFocus : Roo.emptyFn,
21795 hideMode:'offsets',
21799 // blacklist + whitelisted elements..
21806 * Protected method that will not generally be called directly. It
21807 * is called when the editor initializes the iframe with HTML contents. Override this method if you
21808 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21810 getDocMarkup : function(){
21814 // inherit styels from page...??
21815 if (this.stylesheets === false) {
21817 Roo.get(document.head).select('style').each(function(node) {
21818 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21821 Roo.get(document.head).select('link').each(function(node) {
21822 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21825 } else if (!this.stylesheets.length) {
21827 st = '<style type="text/css">' +
21828 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21831 st = '<style type="text/css">' +
21836 st += '<style type="text/css">' +
21837 'IMG { cursor: pointer } ' +
21840 var cls = 'roo-htmleditor-body';
21842 if(this.bodyCls.length){
21843 cls += ' ' + this.bodyCls;
21846 return '<html><head>' + st +
21847 //<style type="text/css">' +
21848 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21850 ' </head><body class="' + cls + '"></body></html>';
21854 onRender : function(ct, position)
21857 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21858 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21861 this.el.dom.style.border = '0 none';
21862 this.el.dom.setAttribute('tabIndex', -1);
21863 this.el.addClass('x-hidden hide');
21867 if(Roo.isIE){ // fix IE 1px bogus margin
21868 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21872 this.frameId = Roo.id();
21876 var iframe = this.owner.wrap.createChild({
21878 cls: 'form-control', // bootstrap..
21880 name: this.frameId,
21881 frameBorder : 'no',
21882 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
21887 this.iframe = iframe.dom;
21889 this.assignDocWin();
21891 this.doc.designMode = 'on';
21894 this.doc.write(this.getDocMarkup());
21898 var task = { // must defer to wait for browser to be ready
21900 //console.log("run task?" + this.doc.readyState);
21901 this.assignDocWin();
21902 if(this.doc.body || this.doc.readyState == 'complete'){
21904 this.doc.designMode="on";
21908 Roo.TaskMgr.stop(task);
21909 this.initEditor.defer(10, this);
21916 Roo.TaskMgr.start(task);
21921 onResize : function(w, h)
21923 Roo.log('resize: ' +w + ',' + h );
21924 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21928 if(typeof w == 'number'){
21930 this.iframe.style.width = w + 'px';
21932 if(typeof h == 'number'){
21934 this.iframe.style.height = h + 'px';
21936 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21943 * Toggles the editor between standard and source edit mode.
21944 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21946 toggleSourceEdit : function(sourceEditMode){
21948 this.sourceEditMode = sourceEditMode === true;
21950 if(this.sourceEditMode){
21952 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
21955 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21956 //this.iframe.className = '';
21959 //this.setSize(this.owner.wrap.getSize());
21960 //this.fireEvent('editmodechange', this, this.sourceEditMode);
21967 * Protected method that will not generally be called directly. If you need/want
21968 * custom HTML cleanup, this is the method you should override.
21969 * @param {String} html The HTML to be cleaned
21970 * return {String} The cleaned HTML
21972 cleanHtml : function(html){
21973 html = String(html);
21974 if(html.length > 5){
21975 if(Roo.isSafari){ // strip safari nonsense
21976 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21979 if(html == ' '){
21986 * HTML Editor -> Textarea
21987 * Protected method that will not generally be called directly. Syncs the contents
21988 * of the editor iframe with the textarea.
21990 syncValue : function(){
21991 if(this.initialized){
21992 var bd = (this.doc.body || this.doc.documentElement);
21993 //this.cleanUpPaste(); -- this is done else where and causes havoc..
21994 var html = bd.innerHTML;
21996 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21997 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21999 html = '<div style="'+m[0]+'">' + html + '</div>';
22002 html = this.cleanHtml(html);
22003 // fix up the special chars.. normaly like back quotes in word...
22004 // however we do not want to do this with chinese..
22005 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22006 var cc = b.charCodeAt();
22008 (cc >= 0x4E00 && cc < 0xA000 ) ||
22009 (cc >= 0x3400 && cc < 0x4E00 ) ||
22010 (cc >= 0xf900 && cc < 0xfb00 )
22016 if(this.owner.fireEvent('beforesync', this, html) !== false){
22017 this.el.dom.value = html;
22018 this.owner.fireEvent('sync', this, html);
22024 * Protected method that will not generally be called directly. Pushes the value of the textarea
22025 * into the iframe editor.
22027 pushValue : function(){
22028 if(this.initialized){
22029 var v = this.el.dom.value.trim();
22031 // if(v.length < 1){
22035 if(this.owner.fireEvent('beforepush', this, v) !== false){
22036 var d = (this.doc.body || this.doc.documentElement);
22038 this.cleanUpPaste();
22039 this.el.dom.value = d.innerHTML;
22040 this.owner.fireEvent('push', this, v);
22046 deferFocus : function(){
22047 this.focus.defer(10, this);
22051 focus : function(){
22052 if(this.win && !this.sourceEditMode){
22059 assignDocWin: function()
22061 var iframe = this.iframe;
22064 this.doc = iframe.contentWindow.document;
22065 this.win = iframe.contentWindow;
22067 // if (!Roo.get(this.frameId)) {
22070 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22071 // this.win = Roo.get(this.frameId).dom.contentWindow;
22073 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22077 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22078 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22083 initEditor : function(){
22084 //console.log("INIT EDITOR");
22085 this.assignDocWin();
22089 this.doc.designMode="on";
22091 this.doc.write(this.getDocMarkup());
22094 var dbody = (this.doc.body || this.doc.documentElement);
22095 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22096 // this copies styles from the containing element into thsi one..
22097 // not sure why we need all of this..
22098 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22100 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22101 //ss['background-attachment'] = 'fixed'; // w3c
22102 dbody.bgProperties = 'fixed'; // ie
22103 //Roo.DomHelper.applyStyles(dbody, ss);
22104 Roo.EventManager.on(this.doc, {
22105 //'mousedown': this.onEditorEvent,
22106 'mouseup': this.onEditorEvent,
22107 'dblclick': this.onEditorEvent,
22108 'click': this.onEditorEvent,
22109 'keyup': this.onEditorEvent,
22114 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22116 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22117 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22119 this.initialized = true;
22121 this.owner.fireEvent('initialize', this);
22126 onDestroy : function(){
22132 //for (var i =0; i < this.toolbars.length;i++) {
22133 // // fixme - ask toolbars for heights?
22134 // this.toolbars[i].onDestroy();
22137 //this.wrap.dom.innerHTML = '';
22138 //this.wrap.remove();
22143 onFirstFocus : function(){
22145 this.assignDocWin();
22148 this.activated = true;
22151 if(Roo.isGecko){ // prevent silly gecko errors
22153 var s = this.win.getSelection();
22154 if(!s.focusNode || s.focusNode.nodeType != 3){
22155 var r = s.getRangeAt(0);
22156 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22161 this.execCmd('useCSS', true);
22162 this.execCmd('styleWithCSS', false);
22165 this.owner.fireEvent('activate', this);
22169 adjustFont: function(btn){
22170 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22171 //if(Roo.isSafari){ // safari
22174 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22175 if(Roo.isSafari){ // safari
22176 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22177 v = (v < 10) ? 10 : v;
22178 v = (v > 48) ? 48 : v;
22179 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22184 v = Math.max(1, v+adjust);
22186 this.execCmd('FontSize', v );
22189 onEditorEvent : function(e)
22191 this.owner.fireEvent('editorevent', this, e);
22192 // this.updateToolbar();
22193 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22196 insertTag : function(tg)
22198 // could be a bit smarter... -> wrap the current selected tRoo..
22199 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22201 range = this.createRange(this.getSelection());
22202 var wrappingNode = this.doc.createElement(tg.toLowerCase());
22203 wrappingNode.appendChild(range.extractContents());
22204 range.insertNode(wrappingNode);
22211 this.execCmd("formatblock", tg);
22215 insertText : function(txt)
22219 var range = this.createRange();
22220 range.deleteContents();
22221 //alert(Sender.getAttribute('label'));
22223 range.insertNode(this.doc.createTextNode(txt));
22229 * Executes a Midas editor command on the editor document and performs necessary focus and
22230 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22231 * @param {String} cmd The Midas command
22232 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22234 relayCmd : function(cmd, value){
22236 this.execCmd(cmd, value);
22237 this.owner.fireEvent('editorevent', this);
22238 //this.updateToolbar();
22239 this.owner.deferFocus();
22243 * Executes a Midas editor command directly on the editor document.
22244 * For visual commands, you should use {@link #relayCmd} instead.
22245 * <b>This should only be called after the editor is initialized.</b>
22246 * @param {String} cmd The Midas command
22247 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22249 execCmd : function(cmd, value){
22250 this.doc.execCommand(cmd, false, value === undefined ? null : value);
22257 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22259 * @param {String} text | dom node..
22261 insertAtCursor : function(text)
22264 if(!this.activated){
22270 var r = this.doc.selection.createRange();
22281 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22285 // from jquery ui (MIT licenced)
22287 var win = this.win;
22289 if (win.getSelection && win.getSelection().getRangeAt) {
22290 range = win.getSelection().getRangeAt(0);
22291 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22292 range.insertNode(node);
22293 } else if (win.document.selection && win.document.selection.createRange) {
22294 // no firefox support
22295 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22296 win.document.selection.createRange().pasteHTML(txt);
22298 // no firefox support
22299 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22300 this.execCmd('InsertHTML', txt);
22309 mozKeyPress : function(e){
22311 var c = e.getCharCode(), cmd;
22314 c = String.fromCharCode(c).toLowerCase();
22328 this.cleanUpPaste.defer(100, this);
22336 e.preventDefault();
22344 fixKeys : function(){ // load time branching for fastest keydown performance
22346 return function(e){
22347 var k = e.getKey(), r;
22350 r = this.doc.selection.createRange();
22353 r.pasteHTML('    ');
22360 r = this.doc.selection.createRange();
22362 var target = r.parentElement();
22363 if(!target || target.tagName.toLowerCase() != 'li'){
22365 r.pasteHTML('<br />');
22371 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22372 this.cleanUpPaste.defer(100, this);
22378 }else if(Roo.isOpera){
22379 return function(e){
22380 var k = e.getKey();
22384 this.execCmd('InsertHTML','    ');
22387 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22388 this.cleanUpPaste.defer(100, this);
22393 }else if(Roo.isSafari){
22394 return function(e){
22395 var k = e.getKey();
22399 this.execCmd('InsertText','\t');
22403 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22404 this.cleanUpPaste.defer(100, this);
22412 getAllAncestors: function()
22414 var p = this.getSelectedNode();
22417 a.push(p); // push blank onto stack..
22418 p = this.getParentElement();
22422 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22426 a.push(this.doc.body);
22430 lastSelNode : false,
22433 getSelection : function()
22435 this.assignDocWin();
22436 return Roo.isIE ? this.doc.selection : this.win.getSelection();
22439 getSelectedNode: function()
22441 // this may only work on Gecko!!!
22443 // should we cache this!!!!
22448 var range = this.createRange(this.getSelection()).cloneRange();
22451 var parent = range.parentElement();
22453 var testRange = range.duplicate();
22454 testRange.moveToElementText(parent);
22455 if (testRange.inRange(range)) {
22458 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22461 parent = parent.parentElement;
22466 // is ancestor a text element.
22467 var ac = range.commonAncestorContainer;
22468 if (ac.nodeType == 3) {
22469 ac = ac.parentNode;
22472 var ar = ac.childNodes;
22475 var other_nodes = [];
22476 var has_other_nodes = false;
22477 for (var i=0;i<ar.length;i++) {
22478 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
22481 // fullly contained node.
22483 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22488 // probably selected..
22489 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22490 other_nodes.push(ar[i]);
22494 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
22499 has_other_nodes = true;
22501 if (!nodes.length && other_nodes.length) {
22502 nodes= other_nodes;
22504 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22510 createRange: function(sel)
22512 // this has strange effects when using with
22513 // top toolbar - not sure if it's a great idea.
22514 //this.editor.contentWindow.focus();
22515 if (typeof sel != "undefined") {
22517 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22519 return this.doc.createRange();
22522 return this.doc.createRange();
22525 getParentElement: function()
22528 this.assignDocWin();
22529 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22531 var range = this.createRange(sel);
22534 var p = range.commonAncestorContainer;
22535 while (p.nodeType == 3) { // text node
22546 * Range intersection.. the hard stuff...
22550 * [ -- selected range --- ]
22554 * if end is before start or hits it. fail.
22555 * if start is after end or hits it fail.
22557 * if either hits (but other is outside. - then it's not
22563 // @see http://www.thismuchiknow.co.uk/?p=64.
22564 rangeIntersectsNode : function(range, node)
22566 var nodeRange = node.ownerDocument.createRange();
22568 nodeRange.selectNode(node);
22570 nodeRange.selectNodeContents(node);
22573 var rangeStartRange = range.cloneRange();
22574 rangeStartRange.collapse(true);
22576 var rangeEndRange = range.cloneRange();
22577 rangeEndRange.collapse(false);
22579 var nodeStartRange = nodeRange.cloneRange();
22580 nodeStartRange.collapse(true);
22582 var nodeEndRange = nodeRange.cloneRange();
22583 nodeEndRange.collapse(false);
22585 return rangeStartRange.compareBoundaryPoints(
22586 Range.START_TO_START, nodeEndRange) == -1 &&
22587 rangeEndRange.compareBoundaryPoints(
22588 Range.START_TO_START, nodeStartRange) == 1;
22592 rangeCompareNode : function(range, node)
22594 var nodeRange = node.ownerDocument.createRange();
22596 nodeRange.selectNode(node);
22598 nodeRange.selectNodeContents(node);
22602 range.collapse(true);
22604 nodeRange.collapse(true);
22606 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22607 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
22609 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22611 var nodeIsBefore = ss == 1;
22612 var nodeIsAfter = ee == -1;
22614 if (nodeIsBefore && nodeIsAfter) {
22617 if (!nodeIsBefore && nodeIsAfter) {
22618 return 1; //right trailed.
22621 if (nodeIsBefore && !nodeIsAfter) {
22622 return 2; // left trailed.
22628 // private? - in a new class?
22629 cleanUpPaste : function()
22631 // cleans up the whole document..
22632 Roo.log('cleanuppaste');
22634 this.cleanUpChildren(this.doc.body);
22635 var clean = this.cleanWordChars(this.doc.body.innerHTML);
22636 if (clean != this.doc.body.innerHTML) {
22637 this.doc.body.innerHTML = clean;
22642 cleanWordChars : function(input) {// change the chars to hex code
22643 var he = Roo.HtmlEditorCore;
22645 var output = input;
22646 Roo.each(he.swapCodes, function(sw) {
22647 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22649 output = output.replace(swapper, sw[1]);
22656 cleanUpChildren : function (n)
22658 if (!n.childNodes.length) {
22661 for (var i = n.childNodes.length-1; i > -1 ; i--) {
22662 this.cleanUpChild(n.childNodes[i]);
22669 cleanUpChild : function (node)
22672 //console.log(node);
22673 if (node.nodeName == "#text") {
22674 // clean up silly Windows -- stuff?
22677 if (node.nodeName == "#comment") {
22678 node.parentNode.removeChild(node);
22679 // clean up silly Windows -- stuff?
22682 var lcname = node.tagName.toLowerCase();
22683 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22684 // whitelist of tags..
22686 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22688 node.parentNode.removeChild(node);
22693 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22695 // remove <a name=....> as rendering on yahoo mailer is borked with this.
22696 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22698 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22699 // remove_keep_children = true;
22702 if (remove_keep_children) {
22703 this.cleanUpChildren(node);
22704 // inserts everything just before this node...
22705 while (node.childNodes.length) {
22706 var cn = node.childNodes[0];
22707 node.removeChild(cn);
22708 node.parentNode.insertBefore(cn, node);
22710 node.parentNode.removeChild(node);
22714 if (!node.attributes || !node.attributes.length) {
22715 this.cleanUpChildren(node);
22719 function cleanAttr(n,v)
22722 if (v.match(/^\./) || v.match(/^\//)) {
22725 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22728 if (v.match(/^#/)) {
22731 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22732 node.removeAttribute(n);
22736 var cwhite = this.cwhite;
22737 var cblack = this.cblack;
22739 function cleanStyle(n,v)
22741 if (v.match(/expression/)) { //XSS?? should we even bother..
22742 node.removeAttribute(n);
22746 var parts = v.split(/;/);
22749 Roo.each(parts, function(p) {
22750 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22754 var l = p.split(':').shift().replace(/\s+/g,'');
22755 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22757 if ( cwhite.length && cblack.indexOf(l) > -1) {
22758 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22759 //node.removeAttribute(n);
22763 // only allow 'c whitelisted system attributes'
22764 if ( cwhite.length && cwhite.indexOf(l) < 0) {
22765 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22766 //node.removeAttribute(n);
22776 if (clean.length) {
22777 node.setAttribute(n, clean.join(';'));
22779 node.removeAttribute(n);
22785 for (var i = node.attributes.length-1; i > -1 ; i--) {
22786 var a = node.attributes[i];
22789 if (a.name.toLowerCase().substr(0,2)=='on') {
22790 node.removeAttribute(a.name);
22793 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22794 node.removeAttribute(a.name);
22797 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22798 cleanAttr(a.name,a.value); // fixme..
22801 if (a.name == 'style') {
22802 cleanStyle(a.name,a.value);
22805 /// clean up MS crap..
22806 // tecnically this should be a list of valid class'es..
22809 if (a.name == 'class') {
22810 if (a.value.match(/^Mso/)) {
22811 node.className = '';
22814 if (a.value.match(/^body$/)) {
22815 node.className = '';
22826 this.cleanUpChildren(node);
22832 * Clean up MS wordisms...
22834 cleanWord : function(node)
22839 this.cleanWord(this.doc.body);
22842 if (node.nodeName == "#text") {
22843 // clean up silly Windows -- stuff?
22846 if (node.nodeName == "#comment") {
22847 node.parentNode.removeChild(node);
22848 // clean up silly Windows -- stuff?
22852 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22853 node.parentNode.removeChild(node);
22857 // remove - but keep children..
22858 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22859 while (node.childNodes.length) {
22860 var cn = node.childNodes[0];
22861 node.removeChild(cn);
22862 node.parentNode.insertBefore(cn, node);
22864 node.parentNode.removeChild(node);
22865 this.iterateChildren(node, this.cleanWord);
22869 if (node.className.length) {
22871 var cn = node.className.split(/\W+/);
22873 Roo.each(cn, function(cls) {
22874 if (cls.match(/Mso[a-zA-Z]+/)) {
22879 node.className = cna.length ? cna.join(' ') : '';
22881 node.removeAttribute("class");
22885 if (node.hasAttribute("lang")) {
22886 node.removeAttribute("lang");
22889 if (node.hasAttribute("style")) {
22891 var styles = node.getAttribute("style").split(";");
22893 Roo.each(styles, function(s) {
22894 if (!s.match(/:/)) {
22897 var kv = s.split(":");
22898 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22901 // what ever is left... we allow.
22904 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22905 if (!nstyle.length) {
22906 node.removeAttribute('style');
22909 this.iterateChildren(node, this.cleanWord);
22915 * iterateChildren of a Node, calling fn each time, using this as the scole..
22916 * @param {DomNode} node node to iterate children of.
22917 * @param {Function} fn method of this class to call on each item.
22919 iterateChildren : function(node, fn)
22921 if (!node.childNodes.length) {
22924 for (var i = node.childNodes.length-1; i > -1 ; i--) {
22925 fn.call(this, node.childNodes[i])
22931 * cleanTableWidths.
22933 * Quite often pasting from word etc.. results in tables with column and widths.
22934 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22937 cleanTableWidths : function(node)
22942 this.cleanTableWidths(this.doc.body);
22947 if (node.nodeName == "#text" || node.nodeName == "#comment") {
22950 Roo.log(node.tagName);
22951 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22952 this.iterateChildren(node, this.cleanTableWidths);
22955 if (node.hasAttribute('width')) {
22956 node.removeAttribute('width');
22960 if (node.hasAttribute("style")) {
22963 var styles = node.getAttribute("style").split(";");
22965 Roo.each(styles, function(s) {
22966 if (!s.match(/:/)) {
22969 var kv = s.split(":");
22970 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22973 // what ever is left... we allow.
22976 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22977 if (!nstyle.length) {
22978 node.removeAttribute('style');
22982 this.iterateChildren(node, this.cleanTableWidths);
22990 domToHTML : function(currentElement, depth, nopadtext) {
22992 depth = depth || 0;
22993 nopadtext = nopadtext || false;
22995 if (!currentElement) {
22996 return this.domToHTML(this.doc.body);
22999 //Roo.log(currentElement);
23001 var allText = false;
23002 var nodeName = currentElement.nodeName;
23003 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23005 if (nodeName == '#text') {
23007 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23012 if (nodeName != 'BODY') {
23015 // Prints the node tagName, such as <A>, <IMG>, etc
23018 for(i = 0; i < currentElement.attributes.length;i++) {
23020 var aname = currentElement.attributes.item(i).name;
23021 if (!currentElement.attributes.item(i).value.length) {
23024 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23027 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23036 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23039 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23044 // Traverse the tree
23046 var currentElementChild = currentElement.childNodes.item(i);
23047 var allText = true;
23048 var innerHTML = '';
23050 while (currentElementChild) {
23051 // Formatting code (indent the tree so it looks nice on the screen)
23052 var nopad = nopadtext;
23053 if (lastnode == 'SPAN') {
23057 if (currentElementChild.nodeName == '#text') {
23058 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23059 toadd = nopadtext ? toadd : toadd.trim();
23060 if (!nopad && toadd.length > 80) {
23061 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
23063 innerHTML += toadd;
23066 currentElementChild = currentElement.childNodes.item(i);
23072 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
23074 // Recursively traverse the tree structure of the child node
23075 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
23076 lastnode = currentElementChild.nodeName;
23078 currentElementChild=currentElement.childNodes.item(i);
23084 // The remaining code is mostly for formatting the tree
23085 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
23090 ret+= "</"+tagName+">";
23096 applyBlacklists : function()
23098 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
23099 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
23103 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23104 if (b.indexOf(tag) > -1) {
23107 this.white.push(tag);
23111 Roo.each(w, function(tag) {
23112 if (b.indexOf(tag) > -1) {
23115 if (this.white.indexOf(tag) > -1) {
23118 this.white.push(tag);
23123 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23124 if (w.indexOf(tag) > -1) {
23127 this.black.push(tag);
23131 Roo.each(b, function(tag) {
23132 if (w.indexOf(tag) > -1) {
23135 if (this.black.indexOf(tag) > -1) {
23138 this.black.push(tag);
23143 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
23144 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
23148 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23149 if (b.indexOf(tag) > -1) {
23152 this.cwhite.push(tag);
23156 Roo.each(w, function(tag) {
23157 if (b.indexOf(tag) > -1) {
23160 if (this.cwhite.indexOf(tag) > -1) {
23163 this.cwhite.push(tag);
23168 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23169 if (w.indexOf(tag) > -1) {
23172 this.cblack.push(tag);
23176 Roo.each(b, function(tag) {
23177 if (w.indexOf(tag) > -1) {
23180 if (this.cblack.indexOf(tag) > -1) {
23183 this.cblack.push(tag);
23188 setStylesheets : function(stylesheets)
23190 if(typeof(stylesheets) == 'string'){
23191 Roo.get(this.iframe.contentDocument.head).createChild({
23193 rel : 'stylesheet',
23202 Roo.each(stylesheets, function(s) {
23207 Roo.get(_this.iframe.contentDocument.head).createChild({
23209 rel : 'stylesheet',
23218 removeStylesheets : function()
23222 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23227 setStyle : function(style)
23229 Roo.get(this.iframe.contentDocument.head).createChild({
23238 // hide stuff that is not compatible
23252 * @event specialkey
23256 * @cfg {String} fieldClass @hide
23259 * @cfg {String} focusClass @hide
23262 * @cfg {String} autoCreate @hide
23265 * @cfg {String} inputType @hide
23268 * @cfg {String} invalidClass @hide
23271 * @cfg {String} invalidText @hide
23274 * @cfg {String} msgFx @hide
23277 * @cfg {String} validateOnBlur @hide
23281 Roo.HtmlEditorCore.white = [
23282 'area', 'br', 'img', 'input', 'hr', 'wbr',
23284 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
23285 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
23286 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
23287 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
23288 'table', 'ul', 'xmp',
23290 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
23293 'dir', 'menu', 'ol', 'ul', 'dl',
23299 Roo.HtmlEditorCore.black = [
23300 // 'embed', 'object', // enable - backend responsiblity to clean thiese
23302 'base', 'basefont', 'bgsound', 'blink', 'body',
23303 'frame', 'frameset', 'head', 'html', 'ilayer',
23304 'iframe', 'layer', 'link', 'meta', 'object',
23305 'script', 'style' ,'title', 'xml' // clean later..
23307 Roo.HtmlEditorCore.clean = [
23308 'script', 'style', 'title', 'xml'
23310 Roo.HtmlEditorCore.remove = [
23315 Roo.HtmlEditorCore.ablack = [
23319 Roo.HtmlEditorCore.aclean = [
23320 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
23324 Roo.HtmlEditorCore.pwhite= [
23325 'http', 'https', 'mailto'
23328 // white listed style attributes.
23329 Roo.HtmlEditorCore.cwhite= [
23330 // 'text-align', /// default is to allow most things..
23336 // black listed style attributes.
23337 Roo.HtmlEditorCore.cblack= [
23338 // 'font-size' -- this can be set by the project
23342 Roo.HtmlEditorCore.swapCodes =[
23361 * @class Roo.bootstrap.HtmlEditor
23362 * @extends Roo.bootstrap.TextArea
23363 * Bootstrap HtmlEditor class
23366 * Create a new HtmlEditor
23367 * @param {Object} config The config object
23370 Roo.bootstrap.HtmlEditor = function(config){
23371 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23372 if (!this.toolbars) {
23373 this.toolbars = [];
23376 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23379 * @event initialize
23380 * Fires when the editor is fully initialized (including the iframe)
23381 * @param {HtmlEditor} this
23386 * Fires when the editor is first receives the focus. Any insertion must wait
23387 * until after this event.
23388 * @param {HtmlEditor} this
23392 * @event beforesync
23393 * Fires before the textarea is updated with content from the editor iframe. Return false
23394 * to cancel the sync.
23395 * @param {HtmlEditor} this
23396 * @param {String} html
23400 * @event beforepush
23401 * Fires before the iframe editor is updated with content from the textarea. Return false
23402 * to cancel the push.
23403 * @param {HtmlEditor} this
23404 * @param {String} html
23409 * Fires when the textarea is updated with content from the editor iframe.
23410 * @param {HtmlEditor} this
23411 * @param {String} html
23416 * Fires when the iframe editor is updated with content from the textarea.
23417 * @param {HtmlEditor} this
23418 * @param {String} html
23422 * @event editmodechange
23423 * Fires when the editor switches edit modes
23424 * @param {HtmlEditor} this
23425 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23427 editmodechange: true,
23429 * @event editorevent
23430 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23431 * @param {HtmlEditor} this
23435 * @event firstfocus
23436 * Fires when on first focus - needed by toolbars..
23437 * @param {HtmlEditor} this
23442 * Auto save the htmlEditor value as a file into Events
23443 * @param {HtmlEditor} this
23447 * @event savedpreview
23448 * preview the saved version of htmlEditor
23449 * @param {HtmlEditor} this
23456 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
23460 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23465 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23470 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
23475 * @cfg {Number} height (in pixels)
23479 * @cfg {Number} width (in pixels)
23484 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23487 stylesheets: false,
23492 // private properties
23493 validationEvent : false,
23495 initialized : false,
23498 onFocus : Roo.emptyFn,
23500 hideMode:'offsets',
23502 tbContainer : false,
23506 toolbarContainer :function() {
23507 return this.wrap.select('.x-html-editor-tb',true).first();
23511 * Protected method that will not generally be called directly. It
23512 * is called when the editor creates its toolbar. Override this method if you need to
23513 * add custom toolbar buttons.
23514 * @param {HtmlEditor} editor
23516 createToolbar : function(){
23517 Roo.log('renewing');
23518 Roo.log("create toolbars");
23520 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23521 this.toolbars[0].render(this.toolbarContainer());
23525 // if (!editor.toolbars || !editor.toolbars.length) {
23526 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23529 // for (var i =0 ; i < editor.toolbars.length;i++) {
23530 // editor.toolbars[i] = Roo.factory(
23531 // typeof(editor.toolbars[i]) == 'string' ?
23532 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
23533 // Roo.bootstrap.HtmlEditor);
23534 // editor.toolbars[i].init(editor);
23540 onRender : function(ct, position)
23542 // Roo.log("Call onRender: " + this.xtype);
23544 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23546 this.wrap = this.inputEl().wrap({
23547 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23550 this.editorcore.onRender(ct, position);
23552 if (this.resizable) {
23553 this.resizeEl = new Roo.Resizable(this.wrap, {
23557 minHeight : this.height,
23558 height: this.height,
23559 handles : this.resizable,
23562 resize : function(r, w, h) {
23563 _t.onResize(w,h); // -something
23569 this.createToolbar(this);
23572 if(!this.width && this.resizable){
23573 this.setSize(this.wrap.getSize());
23575 if (this.resizeEl) {
23576 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23577 // should trigger onReize..
23583 onResize : function(w, h)
23585 Roo.log('resize: ' +w + ',' + h );
23586 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23590 if(this.inputEl() ){
23591 if(typeof w == 'number'){
23592 var aw = w - this.wrap.getFrameWidth('lr');
23593 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23596 if(typeof h == 'number'){
23597 var tbh = -11; // fixme it needs to tool bar size!
23598 for (var i =0; i < this.toolbars.length;i++) {
23599 // fixme - ask toolbars for heights?
23600 tbh += this.toolbars[i].el.getHeight();
23601 //if (this.toolbars[i].footer) {
23602 // tbh += this.toolbars[i].footer.el.getHeight();
23610 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23611 ah -= 5; // knock a few pixes off for look..
23612 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23616 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23617 this.editorcore.onResize(ew,eh);
23622 * Toggles the editor between standard and source edit mode.
23623 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23625 toggleSourceEdit : function(sourceEditMode)
23627 this.editorcore.toggleSourceEdit(sourceEditMode);
23629 if(this.editorcore.sourceEditMode){
23630 Roo.log('editor - showing textarea');
23633 // Roo.log(this.syncValue());
23635 this.inputEl().removeClass(['hide', 'x-hidden']);
23636 this.inputEl().dom.removeAttribute('tabIndex');
23637 this.inputEl().focus();
23639 Roo.log('editor - hiding textarea');
23641 // Roo.log(this.pushValue());
23644 this.inputEl().addClass(['hide', 'x-hidden']);
23645 this.inputEl().dom.setAttribute('tabIndex', -1);
23646 //this.deferFocus();
23649 if(this.resizable){
23650 this.setSize(this.wrap.getSize());
23653 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23656 // private (for BoxComponent)
23657 adjustSize : Roo.BoxComponent.prototype.adjustSize,
23659 // private (for BoxComponent)
23660 getResizeEl : function(){
23664 // private (for BoxComponent)
23665 getPositionEl : function(){
23670 initEvents : function(){
23671 this.originalValue = this.getValue();
23675 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23678 // markInvalid : Roo.emptyFn,
23680 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23683 // clearInvalid : Roo.emptyFn,
23685 setValue : function(v){
23686 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23687 this.editorcore.pushValue();
23692 deferFocus : function(){
23693 this.focus.defer(10, this);
23697 focus : function(){
23698 this.editorcore.focus();
23704 onDestroy : function(){
23710 for (var i =0; i < this.toolbars.length;i++) {
23711 // fixme - ask toolbars for heights?
23712 this.toolbars[i].onDestroy();
23715 this.wrap.dom.innerHTML = '';
23716 this.wrap.remove();
23721 onFirstFocus : function(){
23722 //Roo.log("onFirstFocus");
23723 this.editorcore.onFirstFocus();
23724 for (var i =0; i < this.toolbars.length;i++) {
23725 this.toolbars[i].onFirstFocus();
23731 syncValue : function()
23733 this.editorcore.syncValue();
23736 pushValue : function()
23738 this.editorcore.pushValue();
23742 // hide stuff that is not compatible
23756 * @event specialkey
23760 * @cfg {String} fieldClass @hide
23763 * @cfg {String} focusClass @hide
23766 * @cfg {String} autoCreate @hide
23769 * @cfg {String} inputType @hide
23772 * @cfg {String} invalidClass @hide
23775 * @cfg {String} invalidText @hide
23778 * @cfg {String} msgFx @hide
23781 * @cfg {String} validateOnBlur @hide
23790 Roo.namespace('Roo.bootstrap.htmleditor');
23792 * @class Roo.bootstrap.HtmlEditorToolbar1
23797 new Roo.bootstrap.HtmlEditor({
23800 new Roo.bootstrap.HtmlEditorToolbar1({
23801 disable : { fonts: 1 , format: 1, ..., ... , ...],
23807 * @cfg {Object} disable List of elements to disable..
23808 * @cfg {Array} btns List of additional buttons.
23812 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23815 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23818 Roo.apply(this, config);
23820 // default disabled, based on 'good practice'..
23821 this.disable = this.disable || {};
23822 Roo.applyIf(this.disable, {
23825 specialElements : true
23827 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23829 this.editor = config.editor;
23830 this.editorcore = config.editor.editorcore;
23832 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23834 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23835 // dont call parent... till later.
23837 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
23842 editorcore : false,
23847 "h1","h2","h3","h4","h5","h6",
23849 "abbr", "acronym", "address", "cite", "samp", "var",
23853 onRender : function(ct, position)
23855 // Roo.log("Call onRender: " + this.xtype);
23857 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23859 this.el.dom.style.marginBottom = '0';
23861 var editorcore = this.editorcore;
23862 var editor= this.editor;
23865 var btn = function(id,cmd , toggle, handler, html){
23867 var event = toggle ? 'toggle' : 'click';
23872 xns: Roo.bootstrap,
23875 enableToggle:toggle !== false,
23877 pressed : toggle ? false : null,
23880 a.listeners[toggle ? 'toggle' : 'click'] = function() {
23881 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
23887 // var cb_box = function...
23892 xns: Roo.bootstrap,
23893 glyphicon : 'font',
23897 xns: Roo.bootstrap,
23901 Roo.each(this.formats, function(f) {
23902 style.menu.items.push({
23904 xns: Roo.bootstrap,
23905 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23910 editorcore.insertTag(this.tagname);
23917 children.push(style);
23919 btn('bold',false,true);
23920 btn('italic',false,true);
23921 btn('align-left', 'justifyleft',true);
23922 btn('align-center', 'justifycenter',true);
23923 btn('align-right' , 'justifyright',true);
23924 btn('link', false, false, function(btn) {
23925 //Roo.log("create link?");
23926 var url = prompt(this.createLinkText, this.defaultLinkValue);
23927 if(url && url != 'http:/'+'/'){
23928 this.editorcore.relayCmd('createlink', url);
23931 btn('list','insertunorderedlist',true);
23932 btn('pencil', false,true, function(btn){
23934 this.toggleSourceEdit(btn.pressed);
23937 if (this.editor.btns.length > 0) {
23938 for (var i = 0; i<this.editor.btns.length; i++) {
23939 children.push(this.editor.btns[i]);
23947 xns: Roo.bootstrap,
23952 xns: Roo.bootstrap,
23957 cog.menu.items.push({
23959 xns: Roo.bootstrap,
23960 html : Clean styles,
23965 editorcore.insertTag(this.tagname);
23974 this.xtype = 'NavSimplebar';
23976 for(var i=0;i< children.length;i++) {
23978 this.buttons.add(this.addxtypeChild(children[i]));
23982 editor.on('editorevent', this.updateToolbar, this);
23984 onBtnClick : function(id)
23986 this.editorcore.relayCmd(id);
23987 this.editorcore.focus();
23991 * Protected method that will not generally be called directly. It triggers
23992 * a toolbar update by reading the markup state of the current selection in the editor.
23994 updateToolbar: function(){
23996 if(!this.editorcore.activated){
23997 this.editor.onFirstFocus(); // is this neeed?
24001 var btns = this.buttons;
24002 var doc = this.editorcore.doc;
24003 btns.get('bold').setActive(doc.queryCommandState('bold'));
24004 btns.get('italic').setActive(doc.queryCommandState('italic'));
24005 //btns.get('underline').setActive(doc.queryCommandState('underline'));
24007 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24008 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24009 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24011 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24012 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24015 var ans = this.editorcore.getAllAncestors();
24016 if (this.formatCombo) {
24019 var store = this.formatCombo.store;
24020 this.formatCombo.setValue("");
24021 for (var i =0; i < ans.length;i++) {
24022 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24024 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24032 // hides menus... - so this cant be on a menu...
24033 Roo.bootstrap.MenuMgr.hideAll();
24035 Roo.bootstrap.MenuMgr.hideAll();
24036 //this.editorsyncValue();
24038 onFirstFocus: function() {
24039 this.buttons.each(function(item){
24043 toggleSourceEdit : function(sourceEditMode){
24046 if(sourceEditMode){
24047 Roo.log("disabling buttons");
24048 this.buttons.each( function(item){
24049 if(item.cmd != 'pencil'){
24055 Roo.log("enabling buttons");
24056 if(this.editorcore.initialized){
24057 this.buttons.each( function(item){
24063 Roo.log("calling toggole on editor");
24064 // tell the editor that it's been pressed..
24065 this.editor.toggleSourceEdit(sourceEditMode);
24075 * @class Roo.bootstrap.Table.AbstractSelectionModel
24076 * @extends Roo.util.Observable
24077 * Abstract base class for grid SelectionModels. It provides the interface that should be
24078 * implemented by descendant classes. This class should not be directly instantiated.
24081 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24082 this.locked = false;
24083 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24087 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
24088 /** @ignore Called by the grid automatically. Do not call directly. */
24089 init : function(grid){
24095 * Locks the selections.
24098 this.locked = true;
24102 * Unlocks the selections.
24104 unlock : function(){
24105 this.locked = false;
24109 * Returns true if the selections are locked.
24110 * @return {Boolean}
24112 isLocked : function(){
24113 return this.locked;
24117 * @extends Roo.bootstrap.Table.AbstractSelectionModel
24118 * @class Roo.bootstrap.Table.RowSelectionModel
24119 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24120 * It supports multiple selections and keyboard selection/navigation.
24122 * @param {Object} config
24125 Roo.bootstrap.Table.RowSelectionModel = function(config){
24126 Roo.apply(this, config);
24127 this.selections = new Roo.util.MixedCollection(false, function(o){
24132 this.lastActive = false;
24136 * @event selectionchange
24137 * Fires when the selection changes
24138 * @param {SelectionModel} this
24140 "selectionchange" : true,
24142 * @event afterselectionchange
24143 * Fires after the selection changes (eg. by key press or clicking)
24144 * @param {SelectionModel} this
24146 "afterselectionchange" : true,
24148 * @event beforerowselect
24149 * Fires when a row is selected being selected, return false to cancel.
24150 * @param {SelectionModel} this
24151 * @param {Number} rowIndex The selected index
24152 * @param {Boolean} keepExisting False if other selections will be cleared
24154 "beforerowselect" : true,
24157 * Fires when a row is selected.
24158 * @param {SelectionModel} this
24159 * @param {Number} rowIndex The selected index
24160 * @param {Roo.data.Record} r The record
24162 "rowselect" : true,
24164 * @event rowdeselect
24165 * Fires when a row is deselected.
24166 * @param {SelectionModel} this
24167 * @param {Number} rowIndex The selected index
24169 "rowdeselect" : true
24171 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24172 this.locked = false;
24175 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
24177 * @cfg {Boolean} singleSelect
24178 * True to allow selection of only one row at a time (defaults to false)
24180 singleSelect : false,
24183 initEvents : function()
24186 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24187 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
24188 //}else{ // allow click to work like normal
24189 // this.grid.on("rowclick", this.handleDragableRowClick, this);
24191 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24192 this.grid.on("rowclick", this.handleMouseDown, this);
24194 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24195 "up" : function(e){
24197 this.selectPrevious(e.shiftKey);
24198 }else if(this.last !== false && this.lastActive !== false){
24199 var last = this.last;
24200 this.selectRange(this.last, this.lastActive-1);
24201 this.grid.getView().focusRow(this.lastActive);
24202 if(last !== false){
24206 this.selectFirstRow();
24208 this.fireEvent("afterselectionchange", this);
24210 "down" : function(e){
24212 this.selectNext(e.shiftKey);
24213 }else if(this.last !== false && this.lastActive !== false){
24214 var last = this.last;
24215 this.selectRange(this.last, this.lastActive+1);
24216 this.grid.getView().focusRow(this.lastActive);
24217 if(last !== false){
24221 this.selectFirstRow();
24223 this.fireEvent("afterselectionchange", this);
24227 this.grid.store.on('load', function(){
24228 this.selections.clear();
24231 var view = this.grid.view;
24232 view.on("refresh", this.onRefresh, this);
24233 view.on("rowupdated", this.onRowUpdated, this);
24234 view.on("rowremoved", this.onRemove, this);
24239 onRefresh : function()
24241 var ds = this.grid.store, i, v = this.grid.view;
24242 var s = this.selections;
24243 s.each(function(r){
24244 if((i = ds.indexOfId(r.id)) != -1){
24253 onRemove : function(v, index, r){
24254 this.selections.remove(r);
24258 onRowUpdated : function(v, index, r){
24259 if(this.isSelected(r)){
24260 v.onRowSelect(index);
24266 * @param {Array} records The records to select
24267 * @param {Boolean} keepExisting (optional) True to keep existing selections
24269 selectRecords : function(records, keepExisting)
24272 this.clearSelections();
24274 var ds = this.grid.store;
24275 for(var i = 0, len = records.length; i < len; i++){
24276 this.selectRow(ds.indexOf(records[i]), true);
24281 * Gets the number of selected rows.
24284 getCount : function(){
24285 return this.selections.length;
24289 * Selects the first row in the grid.
24291 selectFirstRow : function(){
24296 * Select the last row.
24297 * @param {Boolean} keepExisting (optional) True to keep existing selections
24299 selectLastRow : function(keepExisting){
24300 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24301 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24305 * Selects the row immediately following the last selected row.
24306 * @param {Boolean} keepExisting (optional) True to keep existing selections
24308 selectNext : function(keepExisting)
24310 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24311 this.selectRow(this.last+1, keepExisting);
24312 this.grid.getView().focusRow(this.last);
24317 * Selects the row that precedes the last selected row.
24318 * @param {Boolean} keepExisting (optional) True to keep existing selections
24320 selectPrevious : function(keepExisting){
24322 this.selectRow(this.last-1, keepExisting);
24323 this.grid.getView().focusRow(this.last);
24328 * Returns the selected records
24329 * @return {Array} Array of selected records
24331 getSelections : function(){
24332 return [].concat(this.selections.items);
24336 * Returns the first selected record.
24339 getSelected : function(){
24340 return this.selections.itemAt(0);
24345 * Clears all selections.
24347 clearSelections : function(fast)
24353 var ds = this.grid.store;
24354 var s = this.selections;
24355 s.each(function(r){
24356 this.deselectRow(ds.indexOfId(r.id));
24360 this.selections.clear();
24367 * Selects all rows.
24369 selectAll : function(){
24373 this.selections.clear();
24374 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24375 this.selectRow(i, true);
24380 * Returns True if there is a selection.
24381 * @return {Boolean}
24383 hasSelection : function(){
24384 return this.selections.length > 0;
24388 * Returns True if the specified row is selected.
24389 * @param {Number/Record} record The record or index of the record to check
24390 * @return {Boolean}
24392 isSelected : function(index){
24393 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24394 return (r && this.selections.key(r.id) ? true : false);
24398 * Returns True if the specified record id is selected.
24399 * @param {String} id The id of record to check
24400 * @return {Boolean}
24402 isIdSelected : function(id){
24403 return (this.selections.key(id) ? true : false);
24408 handleMouseDBClick : function(e, t){
24412 handleMouseDown : function(e, t)
24414 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24415 if(this.isLocked() || rowIndex < 0 ){
24418 if(e.shiftKey && this.last !== false){
24419 var last = this.last;
24420 this.selectRange(last, rowIndex, e.ctrlKey);
24421 this.last = last; // reset the last
24425 var isSelected = this.isSelected(rowIndex);
24426 //Roo.log("select row:" + rowIndex);
24428 this.deselectRow(rowIndex);
24430 this.selectRow(rowIndex, true);
24434 if(e.button !== 0 && isSelected){
24435 alert('rowIndex 2: ' + rowIndex);
24436 view.focusRow(rowIndex);
24437 }else if(e.ctrlKey && isSelected){
24438 this.deselectRow(rowIndex);
24439 }else if(!isSelected){
24440 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24441 view.focusRow(rowIndex);
24445 this.fireEvent("afterselectionchange", this);
24448 handleDragableRowClick : function(grid, rowIndex, e)
24450 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24451 this.selectRow(rowIndex, false);
24452 grid.view.focusRow(rowIndex);
24453 this.fireEvent("afterselectionchange", this);
24458 * Selects multiple rows.
24459 * @param {Array} rows Array of the indexes of the row to select
24460 * @param {Boolean} keepExisting (optional) True to keep existing selections
24462 selectRows : function(rows, keepExisting){
24464 this.clearSelections();
24466 for(var i = 0, len = rows.length; i < len; i++){
24467 this.selectRow(rows[i], true);
24472 * Selects a range of rows. All rows in between startRow and endRow are also selected.
24473 * @param {Number} startRow The index of the first row in the range
24474 * @param {Number} endRow The index of the last row in the range
24475 * @param {Boolean} keepExisting (optional) True to retain existing selections
24477 selectRange : function(startRow, endRow, keepExisting){
24482 this.clearSelections();
24484 if(startRow <= endRow){
24485 for(var i = startRow; i <= endRow; i++){
24486 this.selectRow(i, true);
24489 for(var i = startRow; i >= endRow; i--){
24490 this.selectRow(i, true);
24496 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24497 * @param {Number} startRow The index of the first row in the range
24498 * @param {Number} endRow The index of the last row in the range
24500 deselectRange : function(startRow, endRow, preventViewNotify){
24504 for(var i = startRow; i <= endRow; i++){
24505 this.deselectRow(i, preventViewNotify);
24511 * @param {Number} row The index of the row to select
24512 * @param {Boolean} keepExisting (optional) True to keep existing selections
24514 selectRow : function(index, keepExisting, preventViewNotify)
24516 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24519 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24520 if(!keepExisting || this.singleSelect){
24521 this.clearSelections();
24524 var r = this.grid.store.getAt(index);
24525 //console.log('selectRow - record id :' + r.id);
24527 this.selections.add(r);
24528 this.last = this.lastActive = index;
24529 if(!preventViewNotify){
24530 var proxy = new Roo.Element(
24531 this.grid.getRowDom(index)
24533 proxy.addClass('bg-info info');
24535 this.fireEvent("rowselect", this, index, r);
24536 this.fireEvent("selectionchange", this);
24542 * @param {Number} row The index of the row to deselect
24544 deselectRow : function(index, preventViewNotify)
24549 if(this.last == index){
24552 if(this.lastActive == index){
24553 this.lastActive = false;
24556 var r = this.grid.store.getAt(index);
24561 this.selections.remove(r);
24562 //.console.log('deselectRow - record id :' + r.id);
24563 if(!preventViewNotify){
24565 var proxy = new Roo.Element(
24566 this.grid.getRowDom(index)
24568 proxy.removeClass('bg-info info');
24570 this.fireEvent("rowdeselect", this, index);
24571 this.fireEvent("selectionchange", this);
24575 restoreLast : function(){
24577 this.last = this._last;
24582 acceptsNav : function(row, col, cm){
24583 return !cm.isHidden(col) && cm.isCellEditable(col, row);
24587 onEditorKey : function(field, e){
24588 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24593 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24595 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24597 }else if(k == e.ENTER && !e.ctrlKey){
24601 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24603 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24605 }else if(k == e.ESC){
24609 g.startEditing(newCell[0], newCell[1]);
24615 * Ext JS Library 1.1.1
24616 * Copyright(c) 2006-2007, Ext JS, LLC.
24618 * Originally Released Under LGPL - original licence link has changed is not relivant.
24621 * <script type="text/javascript">
24625 * @class Roo.bootstrap.PagingToolbar
24626 * @extends Roo.bootstrap.NavSimplebar
24627 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24629 * Create a new PagingToolbar
24630 * @param {Object} config The config object
24631 * @param {Roo.data.Store} store
24633 Roo.bootstrap.PagingToolbar = function(config)
24635 // old args format still supported... - xtype is prefered..
24636 // created from xtype...
24638 this.ds = config.dataSource;
24640 if (config.store && !this.ds) {
24641 this.store= Roo.factory(config.store, Roo.data);
24642 this.ds = this.store;
24643 this.ds.xmodule = this.xmodule || false;
24646 this.toolbarItems = [];
24647 if (config.items) {
24648 this.toolbarItems = config.items;
24651 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24656 this.bind(this.ds);
24659 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24663 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24665 * @cfg {Roo.data.Store} dataSource
24666 * The underlying data store providing the paged data
24669 * @cfg {String/HTMLElement/Element} container
24670 * container The id or element that will contain the toolbar
24673 * @cfg {Boolean} displayInfo
24674 * True to display the displayMsg (defaults to false)
24677 * @cfg {Number} pageSize
24678 * The number of records to display per page (defaults to 20)
24682 * @cfg {String} displayMsg
24683 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24685 displayMsg : 'Displaying {0} - {1} of {2}',
24687 * @cfg {String} emptyMsg
24688 * The message to display when no records are found (defaults to "No data to display")
24690 emptyMsg : 'No data to display',
24692 * Customizable piece of the default paging text (defaults to "Page")
24695 beforePageText : "Page",
24697 * Customizable piece of the default paging text (defaults to "of %0")
24700 afterPageText : "of {0}",
24702 * Customizable piece of the default paging text (defaults to "First Page")
24705 firstText : "First Page",
24707 * Customizable piece of the default paging text (defaults to "Previous Page")
24710 prevText : "Previous Page",
24712 * Customizable piece of the default paging text (defaults to "Next Page")
24715 nextText : "Next Page",
24717 * Customizable piece of the default paging text (defaults to "Last Page")
24720 lastText : "Last Page",
24722 * Customizable piece of the default paging text (defaults to "Refresh")
24725 refreshText : "Refresh",
24729 onRender : function(ct, position)
24731 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24732 this.navgroup.parentId = this.id;
24733 this.navgroup.onRender(this.el, null);
24734 // add the buttons to the navgroup
24736 if(this.displayInfo){
24737 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24738 this.displayEl = this.el.select('.x-paging-info', true).first();
24739 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24740 // this.displayEl = navel.el.select('span',true).first();
24746 Roo.each(_this.buttons, function(e){ // this might need to use render????
24747 Roo.factory(e).render(_this.el);
24751 Roo.each(_this.toolbarItems, function(e) {
24752 _this.navgroup.addItem(e);
24756 this.first = this.navgroup.addItem({
24757 tooltip: this.firstText,
24759 icon : 'fa fa-step-backward',
24761 preventDefault: true,
24762 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24765 this.prev = this.navgroup.addItem({
24766 tooltip: this.prevText,
24768 icon : 'fa fa-backward',
24770 preventDefault: true,
24771 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
24773 //this.addSeparator();
24776 var field = this.navgroup.addItem( {
24778 cls : 'x-paging-position',
24780 html : this.beforePageText +
24781 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24782 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
24785 this.field = field.el.select('input', true).first();
24786 this.field.on("keydown", this.onPagingKeydown, this);
24787 this.field.on("focus", function(){this.dom.select();});
24790 this.afterTextEl = field.el.select('.x-paging-after',true).first();
24791 //this.field.setHeight(18);
24792 //this.addSeparator();
24793 this.next = this.navgroup.addItem({
24794 tooltip: this.nextText,
24796 html : ' <i class="fa fa-forward">',
24798 preventDefault: true,
24799 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
24801 this.last = this.navgroup.addItem({
24802 tooltip: this.lastText,
24803 icon : 'fa fa-step-forward',
24806 preventDefault: true,
24807 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
24809 //this.addSeparator();
24810 this.loading = this.navgroup.addItem({
24811 tooltip: this.refreshText,
24812 icon: 'fa fa-refresh',
24813 preventDefault: true,
24814 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24820 updateInfo : function(){
24821 if(this.displayEl){
24822 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24823 var msg = count == 0 ?
24827 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
24829 this.displayEl.update(msg);
24834 onLoad : function(ds, r, o)
24836 this.cursor = o.params.start ? o.params.start : 0;
24838 var d = this.getPageData(),
24843 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24844 this.field.dom.value = ap;
24845 this.first.setDisabled(ap == 1);
24846 this.prev.setDisabled(ap == 1);
24847 this.next.setDisabled(ap == ps);
24848 this.last.setDisabled(ap == ps);
24849 this.loading.enable();
24854 getPageData : function(){
24855 var total = this.ds.getTotalCount();
24858 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24859 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24864 onLoadError : function(){
24865 this.loading.enable();
24869 onPagingKeydown : function(e){
24870 var k = e.getKey();
24871 var d = this.getPageData();
24873 var v = this.field.dom.value, pageNum;
24874 if(!v || isNaN(pageNum = parseInt(v, 10))){
24875 this.field.dom.value = d.activePage;
24878 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24879 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24882 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))
24884 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24885 this.field.dom.value = pageNum;
24886 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24889 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24891 var v = this.field.dom.value, pageNum;
24892 var increment = (e.shiftKey) ? 10 : 1;
24893 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24896 if(!v || isNaN(pageNum = parseInt(v, 10))) {
24897 this.field.dom.value = d.activePage;
24900 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24902 this.field.dom.value = parseInt(v, 10) + increment;
24903 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24904 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24911 beforeLoad : function(){
24913 this.loading.disable();
24918 onClick : function(which){
24927 ds.load({params:{start: 0, limit: this.pageSize}});
24930 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24933 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24936 var total = ds.getTotalCount();
24937 var extra = total % this.pageSize;
24938 var lastStart = extra ? (total - extra) : total-this.pageSize;
24939 ds.load({params:{start: lastStart, limit: this.pageSize}});
24942 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24948 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24949 * @param {Roo.data.Store} store The data store to unbind
24951 unbind : function(ds){
24952 ds.un("beforeload", this.beforeLoad, this);
24953 ds.un("load", this.onLoad, this);
24954 ds.un("loadexception", this.onLoadError, this);
24955 ds.un("remove", this.updateInfo, this);
24956 ds.un("add", this.updateInfo, this);
24957 this.ds = undefined;
24961 * Binds the paging toolbar to the specified {@link Roo.data.Store}
24962 * @param {Roo.data.Store} store The data store to bind
24964 bind : function(ds){
24965 ds.on("beforeload", this.beforeLoad, this);
24966 ds.on("load", this.onLoad, this);
24967 ds.on("loadexception", this.onLoadError, this);
24968 ds.on("remove", this.updateInfo, this);
24969 ds.on("add", this.updateInfo, this);
24980 * @class Roo.bootstrap.MessageBar
24981 * @extends Roo.bootstrap.Component
24982 * Bootstrap MessageBar class
24983 * @cfg {String} html contents of the MessageBar
24984 * @cfg {String} weight (info | success | warning | danger) default info
24985 * @cfg {String} beforeClass insert the bar before the given class
24986 * @cfg {Boolean} closable (true | false) default false
24987 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24990 * Create a new Element
24991 * @param {Object} config The config object
24994 Roo.bootstrap.MessageBar = function(config){
24995 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24998 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
25004 beforeClass: 'bootstrap-sticky-wrap',
25006 getAutoCreate : function(){
25010 cls: 'alert alert-dismissable alert-' + this.weight,
25015 html: this.html || ''
25021 cfg.cls += ' alert-messages-fixed';
25035 onRender : function(ct, position)
25037 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25040 var cfg = Roo.apply({}, this.getAutoCreate());
25044 cfg.cls += ' ' + this.cls;
25047 cfg.style = this.style;
25049 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25051 this.el.setVisibilityMode(Roo.Element.DISPLAY);
25054 this.el.select('>button.close').on('click', this.hide, this);
25060 if (!this.rendered) {
25066 this.fireEvent('show', this);
25072 if (!this.rendered) {
25078 this.fireEvent('hide', this);
25081 update : function()
25083 // var e = this.el.dom.firstChild;
25085 // if(this.closable){
25086 // e = e.nextSibling;
25089 // e.data = this.html || '';
25091 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25107 * @class Roo.bootstrap.Graph
25108 * @extends Roo.bootstrap.Component
25109 * Bootstrap Graph class
25113 @cfg {String} graphtype bar | vbar | pie
25114 @cfg {number} g_x coodinator | centre x (pie)
25115 @cfg {number} g_y coodinator | centre y (pie)
25116 @cfg {number} g_r radius (pie)
25117 @cfg {number} g_height height of the chart (respected by all elements in the set)
25118 @cfg {number} g_width width of the chart (respected by all elements in the set)
25119 @cfg {Object} title The title of the chart
25122 -opts (object) options for the chart
25124 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25125 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25127 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.
25128 o stacked (boolean) whether or not to tread values as in a stacked bar chart
25130 o stretch (boolean)
25132 -opts (object) options for the pie
25135 o startAngle (number)
25136 o endAngle (number)
25140 * Create a new Input
25141 * @param {Object} config The config object
25144 Roo.bootstrap.Graph = function(config){
25145 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25151 * The img click event for the img.
25152 * @param {Roo.EventObject} e
25158 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
25169 //g_colors: this.colors,
25176 getAutoCreate : function(){
25187 onRender : function(ct,position){
25190 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25192 if (typeof(Raphael) == 'undefined') {
25193 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25197 this.raphael = Raphael(this.el.dom);
25199 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25200 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25201 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25202 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25204 r.text(160, 10, "Single Series Chart").attr(txtattr);
25205 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25206 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25207 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25209 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25210 r.barchart(330, 10, 300, 220, data1);
25211 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25212 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25215 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25216 // r.barchart(30, 30, 560, 250, xdata, {
25217 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25218 // axis : "0 0 1 1",
25219 // axisxlabels : xdata
25220 // //yvalues : cols,
25223 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25225 // this.load(null,xdata,{
25226 // axis : "0 0 1 1",
25227 // axisxlabels : xdata
25232 load : function(graphtype,xdata,opts)
25234 this.raphael.clear();
25236 graphtype = this.graphtype;
25241 var r = this.raphael,
25242 fin = function () {
25243 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25245 fout = function () {
25246 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25248 pfin = function() {
25249 this.sector.stop();
25250 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25253 this.label[0].stop();
25254 this.label[0].attr({ r: 7.5 });
25255 this.label[1].attr({ "font-weight": 800 });
25258 pfout = function() {
25259 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25262 this.label[0].animate({ r: 5 }, 500, "bounce");
25263 this.label[1].attr({ "font-weight": 400 });
25269 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25272 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25275 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
25276 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25278 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25285 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25290 setTitle: function(o)
25295 initEvents: function() {
25298 this.el.on('click', this.onClick, this);
25302 onClick : function(e)
25304 Roo.log('img onclick');
25305 this.fireEvent('click', this, e);
25317 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25320 * @class Roo.bootstrap.dash.NumberBox
25321 * @extends Roo.bootstrap.Component
25322 * Bootstrap NumberBox class
25323 * @cfg {String} headline Box headline
25324 * @cfg {String} content Box content
25325 * @cfg {String} icon Box icon
25326 * @cfg {String} footer Footer text
25327 * @cfg {String} fhref Footer href
25330 * Create a new NumberBox
25331 * @param {Object} config The config object
25335 Roo.bootstrap.dash.NumberBox = function(config){
25336 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25340 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
25349 getAutoCreate : function(){
25353 cls : 'small-box ',
25361 cls : 'roo-headline',
25362 html : this.headline
25366 cls : 'roo-content',
25367 html : this.content
25381 cls : 'ion ' + this.icon
25390 cls : 'small-box-footer',
25391 href : this.fhref || '#',
25395 cfg.cn.push(footer);
25402 onRender : function(ct,position){
25403 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25410 setHeadline: function (value)
25412 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25415 setFooter: function (value, href)
25417 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25420 this.el.select('a.small-box-footer',true).first().attr('href', href);
25425 setContent: function (value)
25427 this.el.select('.roo-content',true).first().dom.innerHTML = value;
25430 initEvents: function()
25444 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25447 * @class Roo.bootstrap.dash.TabBox
25448 * @extends Roo.bootstrap.Component
25449 * Bootstrap TabBox class
25450 * @cfg {String} title Title of the TabBox
25451 * @cfg {String} icon Icon of the TabBox
25452 * @cfg {Boolean} showtabs (true|false) show the tabs default true
25453 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25456 * Create a new TabBox
25457 * @param {Object} config The config object
25461 Roo.bootstrap.dash.TabBox = function(config){
25462 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25467 * When a pane is added
25468 * @param {Roo.bootstrap.dash.TabPane} pane
25472 * @event activatepane
25473 * When a pane is activated
25474 * @param {Roo.bootstrap.dash.TabPane} pane
25476 "activatepane" : true
25484 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
25489 tabScrollable : false,
25491 getChildContainer : function()
25493 return this.el.select('.tab-content', true).first();
25496 getAutoCreate : function(){
25500 cls: 'pull-left header',
25508 cls: 'fa ' + this.icon
25514 cls: 'nav nav-tabs pull-right',
25520 if(this.tabScrollable){
25527 cls: 'nav nav-tabs pull-right',
25538 cls: 'nav-tabs-custom',
25543 cls: 'tab-content no-padding',
25551 initEvents : function()
25553 //Roo.log('add add pane handler');
25554 this.on('addpane', this.onAddPane, this);
25557 * Updates the box title
25558 * @param {String} html to set the title to.
25560 setTitle : function(value)
25562 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25564 onAddPane : function(pane)
25566 this.panes.push(pane);
25567 //Roo.log('addpane');
25569 // tabs are rendere left to right..
25570 if(!this.showtabs){
25574 var ctr = this.el.select('.nav-tabs', true).first();
25577 var existing = ctr.select('.nav-tab',true);
25578 var qty = existing.getCount();;
25581 var tab = ctr.createChild({
25583 cls : 'nav-tab' + (qty ? '' : ' active'),
25591 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25594 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25596 pane.el.addClass('active');
25601 onTabClick : function(ev,un,ob,pane)
25603 //Roo.log('tab - prev default');
25604 ev.preventDefault();
25607 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25608 pane.tab.addClass('active');
25609 //Roo.log(pane.title);
25610 this.getChildContainer().select('.tab-pane',true).removeClass('active');
25611 // technically we should have a deactivate event.. but maybe add later.
25612 // and it should not de-activate the selected tab...
25613 this.fireEvent('activatepane', pane);
25614 pane.el.addClass('active');
25615 pane.fireEvent('activate');
25620 getActivePane : function()
25623 Roo.each(this.panes, function(p) {
25624 if(p.el.hasClass('active')){
25645 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25647 * @class Roo.bootstrap.TabPane
25648 * @extends Roo.bootstrap.Component
25649 * Bootstrap TabPane class
25650 * @cfg {Boolean} active (false | true) Default false
25651 * @cfg {String} title title of panel
25655 * Create a new TabPane
25656 * @param {Object} config The config object
25659 Roo.bootstrap.dash.TabPane = function(config){
25660 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25666 * When a pane is activated
25667 * @param {Roo.bootstrap.dash.TabPane} pane
25674 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
25679 // the tabBox that this is attached to.
25682 getAutoCreate : function()
25690 cfg.cls += ' active';
25695 initEvents : function()
25697 //Roo.log('trigger add pane handler');
25698 this.parent().fireEvent('addpane', this)
25702 * Updates the tab title
25703 * @param {String} html to set the title to.
25705 setTitle: function(str)
25711 this.tab.select('a', true).first().dom.innerHTML = str;
25728 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25731 * @class Roo.bootstrap.menu.Menu
25732 * @extends Roo.bootstrap.Component
25733 * Bootstrap Menu class - container for Menu
25734 * @cfg {String} html Text of the menu
25735 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25736 * @cfg {String} icon Font awesome icon
25737 * @cfg {String} pos Menu align to (top | bottom) default bottom
25741 * Create a new Menu
25742 * @param {Object} config The config object
25746 Roo.bootstrap.menu.Menu = function(config){
25747 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25751 * @event beforeshow
25752 * Fires before this menu is displayed
25753 * @param {Roo.bootstrap.menu.Menu} this
25757 * @event beforehide
25758 * Fires before this menu is hidden
25759 * @param {Roo.bootstrap.menu.Menu} this
25764 * Fires after this menu is displayed
25765 * @param {Roo.bootstrap.menu.Menu} this
25770 * Fires after this menu is hidden
25771 * @param {Roo.bootstrap.menu.Menu} this
25776 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25777 * @param {Roo.bootstrap.menu.Menu} this
25778 * @param {Roo.EventObject} e
25785 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
25789 weight : 'default',
25794 getChildContainer : function() {
25795 if(this.isSubMenu){
25799 return this.el.select('ul.dropdown-menu', true).first();
25802 getAutoCreate : function()
25807 cls : 'roo-menu-text',
25815 cls : 'fa ' + this.icon
25826 cls : 'dropdown-button btn btn-' + this.weight,
25831 cls : 'dropdown-toggle btn btn-' + this.weight,
25841 cls : 'dropdown-menu'
25847 if(this.pos == 'top'){
25848 cfg.cls += ' dropup';
25851 if(this.isSubMenu){
25854 cls : 'dropdown-menu'
25861 onRender : function(ct, position)
25863 this.isSubMenu = ct.hasClass('dropdown-submenu');
25865 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25868 initEvents : function()
25870 if(this.isSubMenu){
25874 this.hidden = true;
25876 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25877 this.triggerEl.on('click', this.onTriggerPress, this);
25879 this.buttonEl = this.el.select('button.dropdown-button', true).first();
25880 this.buttonEl.on('click', this.onClick, this);
25886 if(this.isSubMenu){
25890 return this.el.select('ul.dropdown-menu', true).first();
25893 onClick : function(e)
25895 this.fireEvent("click", this, e);
25898 onTriggerPress : function(e)
25900 if (this.isVisible()) {
25907 isVisible : function(){
25908 return !this.hidden;
25913 this.fireEvent("beforeshow", this);
25915 this.hidden = false;
25916 this.el.addClass('open');
25918 Roo.get(document).on("mouseup", this.onMouseUp, this);
25920 this.fireEvent("show", this);
25927 this.fireEvent("beforehide", this);
25929 this.hidden = true;
25930 this.el.removeClass('open');
25932 Roo.get(document).un("mouseup", this.onMouseUp);
25934 this.fireEvent("hide", this);
25937 onMouseUp : function()
25951 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25954 * @class Roo.bootstrap.menu.Item
25955 * @extends Roo.bootstrap.Component
25956 * Bootstrap MenuItem class
25957 * @cfg {Boolean} submenu (true | false) default false
25958 * @cfg {String} html text of the item
25959 * @cfg {String} href the link
25960 * @cfg {Boolean} disable (true | false) default false
25961 * @cfg {Boolean} preventDefault (true | false) default true
25962 * @cfg {String} icon Font awesome icon
25963 * @cfg {String} pos Submenu align to (left | right) default right
25967 * Create a new Item
25968 * @param {Object} config The config object
25972 Roo.bootstrap.menu.Item = function(config){
25973 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25977 * Fires when the mouse is hovering over this menu
25978 * @param {Roo.bootstrap.menu.Item} this
25979 * @param {Roo.EventObject} e
25984 * Fires when the mouse exits this menu
25985 * @param {Roo.bootstrap.menu.Item} this
25986 * @param {Roo.EventObject} e
25992 * The raw click event for the entire grid.
25993 * @param {Roo.EventObject} e
25999 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
26004 preventDefault: true,
26009 getAutoCreate : function()
26014 cls : 'roo-menu-item-text',
26022 cls : 'fa ' + this.icon
26031 href : this.href || '#',
26038 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26042 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26044 if(this.pos == 'left'){
26045 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26052 initEvents : function()
26054 this.el.on('mouseover', this.onMouseOver, this);
26055 this.el.on('mouseout', this.onMouseOut, this);
26057 this.el.select('a', true).first().on('click', this.onClick, this);
26061 onClick : function(e)
26063 if(this.preventDefault){
26064 e.preventDefault();
26067 this.fireEvent("click", this, e);
26070 onMouseOver : function(e)
26072 if(this.submenu && this.pos == 'left'){
26073 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26076 this.fireEvent("mouseover", this, e);
26079 onMouseOut : function(e)
26081 this.fireEvent("mouseout", this, e);
26093 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26096 * @class Roo.bootstrap.menu.Separator
26097 * @extends Roo.bootstrap.Component
26098 * Bootstrap Separator class
26101 * Create a new Separator
26102 * @param {Object} config The config object
26106 Roo.bootstrap.menu.Separator = function(config){
26107 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26110 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
26112 getAutoCreate : function(){
26133 * @class Roo.bootstrap.Tooltip
26134 * Bootstrap Tooltip class
26135 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26136 * to determine which dom element triggers the tooltip.
26138 * It needs to add support for additional attributes like tooltip-position
26141 * Create a new Toolti
26142 * @param {Object} config The config object
26145 Roo.bootstrap.Tooltip = function(config){
26146 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26148 this.alignment = Roo.bootstrap.Tooltip.alignment;
26150 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26151 this.alignment = config.alignment;
26156 Roo.apply(Roo.bootstrap.Tooltip, {
26158 * @function init initialize tooltip monitoring.
26162 currentTip : false,
26163 currentRegion : false,
26169 Roo.get(document).on('mouseover', this.enter ,this);
26170 Roo.get(document).on('mouseout', this.leave, this);
26173 this.currentTip = new Roo.bootstrap.Tooltip();
26176 enter : function(ev)
26178 var dom = ev.getTarget();
26180 //Roo.log(['enter',dom]);
26181 var el = Roo.fly(dom);
26182 if (this.currentEl) {
26184 //Roo.log(this.currentEl);
26185 //Roo.log(this.currentEl.contains(dom));
26186 if (this.currentEl == el) {
26189 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26195 if (this.currentTip.el) {
26196 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26200 if(!el || el.dom == document){
26206 // you can not look for children, as if el is the body.. then everythign is the child..
26207 if (!el.attr('tooltip')) { //
26208 if (!el.select("[tooltip]").elements.length) {
26211 // is the mouse over this child...?
26212 bindEl = el.select("[tooltip]").first();
26213 var xy = ev.getXY();
26214 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26215 //Roo.log("not in region.");
26218 //Roo.log("child element over..");
26221 this.currentEl = bindEl;
26222 this.currentTip.bind(bindEl);
26223 this.currentRegion = Roo.lib.Region.getRegion(dom);
26224 this.currentTip.enter();
26227 leave : function(ev)
26229 var dom = ev.getTarget();
26230 //Roo.log(['leave',dom]);
26231 if (!this.currentEl) {
26236 if (dom != this.currentEl.dom) {
26239 var xy = ev.getXY();
26240 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
26243 // only activate leave if mouse cursor is outside... bounding box..
26248 if (this.currentTip) {
26249 this.currentTip.leave();
26251 //Roo.log('clear currentEl');
26252 this.currentEl = false;
26257 'left' : ['r-l', [-2,0], 'right'],
26258 'right' : ['l-r', [2,0], 'left'],
26259 'bottom' : ['t-b', [0,2], 'top'],
26260 'top' : [ 'b-t', [0,-2], 'bottom']
26266 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
26271 delay : null, // can be { show : 300 , hide: 500}
26275 hoverState : null, //???
26277 placement : 'bottom',
26281 getAutoCreate : function(){
26288 cls : 'tooltip-arrow'
26291 cls : 'tooltip-inner'
26298 bind : function(el)
26304 enter : function () {
26306 if (this.timeout != null) {
26307 clearTimeout(this.timeout);
26310 this.hoverState = 'in';
26311 //Roo.log("enter - show");
26312 if (!this.delay || !this.delay.show) {
26317 this.timeout = setTimeout(function () {
26318 if (_t.hoverState == 'in') {
26321 }, this.delay.show);
26325 clearTimeout(this.timeout);
26327 this.hoverState = 'out';
26328 if (!this.delay || !this.delay.hide) {
26334 this.timeout = setTimeout(function () {
26335 //Roo.log("leave - timeout");
26337 if (_t.hoverState == 'out') {
26339 Roo.bootstrap.Tooltip.currentEl = false;
26344 show : function (msg)
26347 this.render(document.body);
26350 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26352 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26354 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26356 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26358 var placement = typeof this.placement == 'function' ?
26359 this.placement.call(this, this.el, on_el) :
26362 var autoToken = /\s?auto?\s?/i;
26363 var autoPlace = autoToken.test(placement);
26365 placement = placement.replace(autoToken, '') || 'top';
26369 //this.el.setXY([0,0]);
26371 //this.el.dom.style.display='block';
26373 //this.el.appendTo(on_el);
26375 var p = this.getPosition();
26376 var box = this.el.getBox();
26382 var align = this.alignment[placement];
26384 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26386 if(placement == 'top' || placement == 'bottom'){
26388 placement = 'right';
26391 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26392 placement = 'left';
26395 var scroll = Roo.select('body', true).first().getScroll();
26397 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26401 align = this.alignment[placement];
26404 this.el.alignTo(this.bindEl, align[0],align[1]);
26405 //var arrow = this.el.select('.arrow',true).first();
26406 //arrow.set(align[2],
26408 this.el.addClass(placement);
26410 this.el.addClass('in fade');
26412 this.hoverState = null;
26414 if (this.el.hasClass('fade')) {
26425 //this.el.setXY([0,0]);
26426 this.el.removeClass('in');
26442 * @class Roo.bootstrap.LocationPicker
26443 * @extends Roo.bootstrap.Component
26444 * Bootstrap LocationPicker class
26445 * @cfg {Number} latitude Position when init default 0
26446 * @cfg {Number} longitude Position when init default 0
26447 * @cfg {Number} zoom default 15
26448 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26449 * @cfg {Boolean} mapTypeControl default false
26450 * @cfg {Boolean} disableDoubleClickZoom default false
26451 * @cfg {Boolean} scrollwheel default true
26452 * @cfg {Boolean} streetViewControl default false
26453 * @cfg {Number} radius default 0
26454 * @cfg {String} locationName
26455 * @cfg {Boolean} draggable default true
26456 * @cfg {Boolean} enableAutocomplete default false
26457 * @cfg {Boolean} enableReverseGeocode default true
26458 * @cfg {String} markerTitle
26461 * Create a new LocationPicker
26462 * @param {Object} config The config object
26466 Roo.bootstrap.LocationPicker = function(config){
26468 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26473 * Fires when the picker initialized.
26474 * @param {Roo.bootstrap.LocationPicker} this
26475 * @param {Google Location} location
26479 * @event positionchanged
26480 * Fires when the picker position changed.
26481 * @param {Roo.bootstrap.LocationPicker} this
26482 * @param {Google Location} location
26484 positionchanged : true,
26487 * Fires when the map resize.
26488 * @param {Roo.bootstrap.LocationPicker} this
26493 * Fires when the map show.
26494 * @param {Roo.bootstrap.LocationPicker} this
26499 * Fires when the map hide.
26500 * @param {Roo.bootstrap.LocationPicker} this
26505 * Fires when click the map.
26506 * @param {Roo.bootstrap.LocationPicker} this
26507 * @param {Map event} e
26511 * @event mapRightClick
26512 * Fires when right click the map.
26513 * @param {Roo.bootstrap.LocationPicker} this
26514 * @param {Map event} e
26516 mapRightClick : true,
26518 * @event markerClick
26519 * Fires when click the marker.
26520 * @param {Roo.bootstrap.LocationPicker} this
26521 * @param {Map event} e
26523 markerClick : true,
26525 * @event markerRightClick
26526 * Fires when right click the marker.
26527 * @param {Roo.bootstrap.LocationPicker} this
26528 * @param {Map event} e
26530 markerRightClick : true,
26532 * @event OverlayViewDraw
26533 * Fires when OverlayView Draw
26534 * @param {Roo.bootstrap.LocationPicker} this
26536 OverlayViewDraw : true,
26538 * @event OverlayViewOnAdd
26539 * Fires when OverlayView Draw
26540 * @param {Roo.bootstrap.LocationPicker} this
26542 OverlayViewOnAdd : true,
26544 * @event OverlayViewOnRemove
26545 * Fires when OverlayView Draw
26546 * @param {Roo.bootstrap.LocationPicker} this
26548 OverlayViewOnRemove : true,
26550 * @event OverlayViewShow
26551 * Fires when OverlayView Draw
26552 * @param {Roo.bootstrap.LocationPicker} this
26553 * @param {Pixel} cpx
26555 OverlayViewShow : true,
26557 * @event OverlayViewHide
26558 * Fires when OverlayView Draw
26559 * @param {Roo.bootstrap.LocationPicker} this
26561 OverlayViewHide : true,
26563 * @event loadexception
26564 * Fires when load google lib failed.
26565 * @param {Roo.bootstrap.LocationPicker} this
26567 loadexception : true
26572 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
26574 gMapContext: false,
26580 mapTypeControl: false,
26581 disableDoubleClickZoom: false,
26583 streetViewControl: false,
26587 enableAutocomplete: false,
26588 enableReverseGeocode: true,
26591 getAutoCreate: function()
26596 cls: 'roo-location-picker'
26602 initEvents: function(ct, position)
26604 if(!this.el.getWidth() || this.isApplied()){
26608 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26613 initial: function()
26615 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26616 this.fireEvent('loadexception', this);
26620 if(!this.mapTypeId){
26621 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26624 this.gMapContext = this.GMapContext();
26626 this.initOverlayView();
26628 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26632 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26633 _this.setPosition(_this.gMapContext.marker.position);
26636 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26637 _this.fireEvent('mapClick', this, event);
26641 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26642 _this.fireEvent('mapRightClick', this, event);
26646 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26647 _this.fireEvent('markerClick', this, event);
26651 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26652 _this.fireEvent('markerRightClick', this, event);
26656 this.setPosition(this.gMapContext.location);
26658 this.fireEvent('initial', this, this.gMapContext.location);
26661 initOverlayView: function()
26665 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26669 _this.fireEvent('OverlayViewDraw', _this);
26674 _this.fireEvent('OverlayViewOnAdd', _this);
26677 onRemove: function()
26679 _this.fireEvent('OverlayViewOnRemove', _this);
26682 show: function(cpx)
26684 _this.fireEvent('OverlayViewShow', _this, cpx);
26689 _this.fireEvent('OverlayViewHide', _this);
26695 fromLatLngToContainerPixel: function(event)
26697 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26700 isApplied: function()
26702 return this.getGmapContext() == false ? false : true;
26705 getGmapContext: function()
26707 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26710 GMapContext: function()
26712 var position = new google.maps.LatLng(this.latitude, this.longitude);
26714 var _map = new google.maps.Map(this.el.dom, {
26717 mapTypeId: this.mapTypeId,
26718 mapTypeControl: this.mapTypeControl,
26719 disableDoubleClickZoom: this.disableDoubleClickZoom,
26720 scrollwheel: this.scrollwheel,
26721 streetViewControl: this.streetViewControl,
26722 locationName: this.locationName,
26723 draggable: this.draggable,
26724 enableAutocomplete: this.enableAutocomplete,
26725 enableReverseGeocode: this.enableReverseGeocode
26728 var _marker = new google.maps.Marker({
26729 position: position,
26731 title: this.markerTitle,
26732 draggable: this.draggable
26739 location: position,
26740 radius: this.radius,
26741 locationName: this.locationName,
26742 addressComponents: {
26743 formatted_address: null,
26744 addressLine1: null,
26745 addressLine2: null,
26747 streetNumber: null,
26751 stateOrProvince: null
26754 domContainer: this.el.dom,
26755 geodecoder: new google.maps.Geocoder()
26759 drawCircle: function(center, radius, options)
26761 if (this.gMapContext.circle != null) {
26762 this.gMapContext.circle.setMap(null);
26766 options = Roo.apply({}, options, {
26767 strokeColor: "#0000FF",
26768 strokeOpacity: .35,
26770 fillColor: "#0000FF",
26774 options.map = this.gMapContext.map;
26775 options.radius = radius;
26776 options.center = center;
26777 this.gMapContext.circle = new google.maps.Circle(options);
26778 return this.gMapContext.circle;
26784 setPosition: function(location)
26786 this.gMapContext.location = location;
26787 this.gMapContext.marker.setPosition(location);
26788 this.gMapContext.map.panTo(location);
26789 this.drawCircle(location, this.gMapContext.radius, {});
26793 if (this.gMapContext.settings.enableReverseGeocode) {
26794 this.gMapContext.geodecoder.geocode({
26795 latLng: this.gMapContext.location
26796 }, function(results, status) {
26798 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26799 _this.gMapContext.locationName = results[0].formatted_address;
26800 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26802 _this.fireEvent('positionchanged', this, location);
26809 this.fireEvent('positionchanged', this, location);
26814 google.maps.event.trigger(this.gMapContext.map, "resize");
26816 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26818 this.fireEvent('resize', this);
26821 setPositionByLatLng: function(latitude, longitude)
26823 this.setPosition(new google.maps.LatLng(latitude, longitude));
26826 getCurrentPosition: function()
26829 latitude: this.gMapContext.location.lat(),
26830 longitude: this.gMapContext.location.lng()
26834 getAddressName: function()
26836 return this.gMapContext.locationName;
26839 getAddressComponents: function()
26841 return this.gMapContext.addressComponents;
26844 address_component_from_google_geocode: function(address_components)
26848 for (var i = 0; i < address_components.length; i++) {
26849 var component = address_components[i];
26850 if (component.types.indexOf("postal_code") >= 0) {
26851 result.postalCode = component.short_name;
26852 } else if (component.types.indexOf("street_number") >= 0) {
26853 result.streetNumber = component.short_name;
26854 } else if (component.types.indexOf("route") >= 0) {
26855 result.streetName = component.short_name;
26856 } else if (component.types.indexOf("neighborhood") >= 0) {
26857 result.city = component.short_name;
26858 } else if (component.types.indexOf("locality") >= 0) {
26859 result.city = component.short_name;
26860 } else if (component.types.indexOf("sublocality") >= 0) {
26861 result.district = component.short_name;
26862 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26863 result.stateOrProvince = component.short_name;
26864 } else if (component.types.indexOf("country") >= 0) {
26865 result.country = component.short_name;
26869 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26870 result.addressLine2 = "";
26874 setZoomLevel: function(zoom)
26876 this.gMapContext.map.setZoom(zoom);
26889 this.fireEvent('show', this);
26900 this.fireEvent('hide', this);
26905 Roo.apply(Roo.bootstrap.LocationPicker, {
26907 OverlayView : function(map, options)
26909 options = options || {};
26923 * @class Roo.bootstrap.Alert
26924 * @extends Roo.bootstrap.Component
26925 * Bootstrap Alert class
26926 * @cfg {String} title The title of alert
26927 * @cfg {String} html The content of alert
26928 * @cfg {String} weight ( success | info | warning | danger )
26929 * @cfg {String} faicon font-awesomeicon
26932 * Create a new alert
26933 * @param {Object} config The config object
26937 Roo.bootstrap.Alert = function(config){
26938 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26942 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
26949 getAutoCreate : function()
26958 cls : 'roo-alert-icon'
26963 cls : 'roo-alert-title',
26968 cls : 'roo-alert-text',
26975 cfg.cn[0].cls += ' fa ' + this.faicon;
26979 cfg.cls += ' alert-' + this.weight;
26985 initEvents: function()
26987 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26990 setTitle : function(str)
26992 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26995 setText : function(str)
26997 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27000 setWeight : function(weight)
27003 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27006 this.weight = weight;
27008 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27011 setIcon : function(icon)
27014 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27017 this.faicon = icon;
27019 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27040 * @class Roo.bootstrap.UploadCropbox
27041 * @extends Roo.bootstrap.Component
27042 * Bootstrap UploadCropbox class
27043 * @cfg {String} emptyText show when image has been loaded
27044 * @cfg {String} rotateNotify show when image too small to rotate
27045 * @cfg {Number} errorTimeout default 3000
27046 * @cfg {Number} minWidth default 300
27047 * @cfg {Number} minHeight default 300
27048 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27049 * @cfg {Boolean} isDocument (true|false) default false
27050 * @cfg {String} url action url
27051 * @cfg {String} paramName default 'imageUpload'
27052 * @cfg {String} method default POST
27053 * @cfg {Boolean} loadMask (true|false) default true
27054 * @cfg {Boolean} loadingText default 'Loading...'
27057 * Create a new UploadCropbox
27058 * @param {Object} config The config object
27061 Roo.bootstrap.UploadCropbox = function(config){
27062 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27066 * @event beforeselectfile
27067 * Fire before select file
27068 * @param {Roo.bootstrap.UploadCropbox} this
27070 "beforeselectfile" : true,
27073 * Fire after initEvent
27074 * @param {Roo.bootstrap.UploadCropbox} this
27079 * Fire after initEvent
27080 * @param {Roo.bootstrap.UploadCropbox} this
27081 * @param {String} data
27086 * Fire when preparing the file data
27087 * @param {Roo.bootstrap.UploadCropbox} this
27088 * @param {Object} file
27093 * Fire when get exception
27094 * @param {Roo.bootstrap.UploadCropbox} this
27095 * @param {XMLHttpRequest} xhr
27097 "exception" : true,
27099 * @event beforeloadcanvas
27100 * Fire before load the canvas
27101 * @param {Roo.bootstrap.UploadCropbox} this
27102 * @param {String} src
27104 "beforeloadcanvas" : true,
27107 * Fire when trash image
27108 * @param {Roo.bootstrap.UploadCropbox} this
27113 * Fire when download the image
27114 * @param {Roo.bootstrap.UploadCropbox} this
27118 * @event footerbuttonclick
27119 * Fire when footerbuttonclick
27120 * @param {Roo.bootstrap.UploadCropbox} this
27121 * @param {String} type
27123 "footerbuttonclick" : true,
27127 * @param {Roo.bootstrap.UploadCropbox} this
27132 * Fire when rotate the image
27133 * @param {Roo.bootstrap.UploadCropbox} this
27134 * @param {String} pos
27139 * Fire when inspect the file
27140 * @param {Roo.bootstrap.UploadCropbox} this
27141 * @param {Object} file
27146 * Fire when xhr upload the file
27147 * @param {Roo.bootstrap.UploadCropbox} this
27148 * @param {Object} data
27153 * Fire when arrange the file data
27154 * @param {Roo.bootstrap.UploadCropbox} this
27155 * @param {Object} formData
27160 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27163 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
27165 emptyText : 'Click to upload image',
27166 rotateNotify : 'Image is too small to rotate',
27167 errorTimeout : 3000,
27181 cropType : 'image/jpeg',
27183 canvasLoaded : false,
27184 isDocument : false,
27186 paramName : 'imageUpload',
27188 loadingText : 'Loading...',
27191 getAutoCreate : function()
27195 cls : 'roo-upload-cropbox',
27199 cls : 'roo-upload-cropbox-selector',
27204 cls : 'roo-upload-cropbox-body',
27205 style : 'cursor:pointer',
27209 cls : 'roo-upload-cropbox-preview'
27213 cls : 'roo-upload-cropbox-thumb'
27217 cls : 'roo-upload-cropbox-empty-notify',
27218 html : this.emptyText
27222 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27223 html : this.rotateNotify
27229 cls : 'roo-upload-cropbox-footer',
27232 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27242 onRender : function(ct, position)
27244 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27246 if (this.buttons.length) {
27248 Roo.each(this.buttons, function(bb) {
27250 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27252 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27258 this.maskEl = this.el;
27262 initEvents : function()
27264 this.urlAPI = (window.createObjectURL && window) ||
27265 (window.URL && URL.revokeObjectURL && URL) ||
27266 (window.webkitURL && webkitURL);
27268 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27269 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27271 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27272 this.selectorEl.hide();
27274 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27275 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27277 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27278 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27279 this.thumbEl.hide();
27281 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27282 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27284 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27285 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27286 this.errorEl.hide();
27288 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27289 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27290 this.footerEl.hide();
27292 this.setThumbBoxSize();
27298 this.fireEvent('initial', this);
27305 window.addEventListener("resize", function() { _this.resize(); } );
27307 this.bodyEl.on('click', this.beforeSelectFile, this);
27310 this.bodyEl.on('touchstart', this.onTouchStart, this);
27311 this.bodyEl.on('touchmove', this.onTouchMove, this);
27312 this.bodyEl.on('touchend', this.onTouchEnd, this);
27316 this.bodyEl.on('mousedown', this.onMouseDown, this);
27317 this.bodyEl.on('mousemove', this.onMouseMove, this);
27318 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27319 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27320 Roo.get(document).on('mouseup', this.onMouseUp, this);
27323 this.selectorEl.on('change', this.onFileSelected, this);
27329 this.baseScale = 1;
27331 this.baseRotate = 1;
27332 this.dragable = false;
27333 this.pinching = false;
27336 this.cropData = false;
27337 this.notifyEl.dom.innerHTML = this.emptyText;
27339 this.selectorEl.dom.value = '';
27343 resize : function()
27345 if(this.fireEvent('resize', this) != false){
27346 this.setThumbBoxPosition();
27347 this.setCanvasPosition();
27351 onFooterButtonClick : function(e, el, o, type)
27354 case 'rotate-left' :
27355 this.onRotateLeft(e);
27357 case 'rotate-right' :
27358 this.onRotateRight(e);
27361 this.beforeSelectFile(e);
27376 this.fireEvent('footerbuttonclick', this, type);
27379 beforeSelectFile : function(e)
27381 e.preventDefault();
27383 if(this.fireEvent('beforeselectfile', this) != false){
27384 this.selectorEl.dom.click();
27388 onFileSelected : function(e)
27390 e.preventDefault();
27392 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27396 var file = this.selectorEl.dom.files[0];
27398 if(this.fireEvent('inspect', this, file) != false){
27399 this.prepare(file);
27404 trash : function(e)
27406 this.fireEvent('trash', this);
27409 download : function(e)
27411 this.fireEvent('download', this);
27414 loadCanvas : function(src)
27416 if(this.fireEvent('beforeloadcanvas', this, src) != false){
27420 this.imageEl = document.createElement('img');
27424 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27426 this.imageEl.src = src;
27430 onLoadCanvas : function()
27432 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27433 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27435 this.bodyEl.un('click', this.beforeSelectFile, this);
27437 this.notifyEl.hide();
27438 this.thumbEl.show();
27439 this.footerEl.show();
27441 this.baseRotateLevel();
27443 if(this.isDocument){
27444 this.setThumbBoxSize();
27447 this.setThumbBoxPosition();
27449 this.baseScaleLevel();
27455 this.canvasLoaded = true;
27458 this.maskEl.unmask();
27463 setCanvasPosition : function()
27465 if(!this.canvasEl){
27469 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27470 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27472 this.previewEl.setLeft(pw);
27473 this.previewEl.setTop(ph);
27477 onMouseDown : function(e)
27481 this.dragable = true;
27482 this.pinching = false;
27484 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27485 this.dragable = false;
27489 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27490 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27494 onMouseMove : function(e)
27498 if(!this.canvasLoaded){
27502 if (!this.dragable){
27506 var minX = Math.ceil(this.thumbEl.getLeft(true));
27507 var minY = Math.ceil(this.thumbEl.getTop(true));
27509 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27510 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27512 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27513 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27515 x = x - this.mouseX;
27516 y = y - this.mouseY;
27518 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27519 var bgY = Math.ceil(y + this.previewEl.getTop(true));
27521 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27522 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27524 this.previewEl.setLeft(bgX);
27525 this.previewEl.setTop(bgY);
27527 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27528 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27531 onMouseUp : function(e)
27535 this.dragable = false;
27538 onMouseWheel : function(e)
27542 this.startScale = this.scale;
27544 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27546 if(!this.zoomable()){
27547 this.scale = this.startScale;
27556 zoomable : function()
27558 var minScale = this.thumbEl.getWidth() / this.minWidth;
27560 if(this.minWidth < this.minHeight){
27561 minScale = this.thumbEl.getHeight() / this.minHeight;
27564 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27565 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27569 (this.rotate == 0 || this.rotate == 180) &&
27571 width > this.imageEl.OriginWidth ||
27572 height > this.imageEl.OriginHeight ||
27573 (width < this.minWidth && height < this.minHeight)
27581 (this.rotate == 90 || this.rotate == 270) &&
27583 width > this.imageEl.OriginWidth ||
27584 height > this.imageEl.OriginHeight ||
27585 (width < this.minHeight && height < this.minWidth)
27592 !this.isDocument &&
27593 (this.rotate == 0 || this.rotate == 180) &&
27595 width < this.minWidth ||
27596 width > this.imageEl.OriginWidth ||
27597 height < this.minHeight ||
27598 height > this.imageEl.OriginHeight
27605 !this.isDocument &&
27606 (this.rotate == 90 || this.rotate == 270) &&
27608 width < this.minHeight ||
27609 width > this.imageEl.OriginWidth ||
27610 height < this.minWidth ||
27611 height > this.imageEl.OriginHeight
27621 onRotateLeft : function(e)
27623 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27625 var minScale = this.thumbEl.getWidth() / this.minWidth;
27627 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27628 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27630 this.startScale = this.scale;
27632 while (this.getScaleLevel() < minScale){
27634 this.scale = this.scale + 1;
27636 if(!this.zoomable()){
27641 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27642 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27647 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27654 this.scale = this.startScale;
27656 this.onRotateFail();
27661 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27663 if(this.isDocument){
27664 this.setThumbBoxSize();
27665 this.setThumbBoxPosition();
27666 this.setCanvasPosition();
27671 this.fireEvent('rotate', this, 'left');
27675 onRotateRight : function(e)
27677 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27679 var minScale = this.thumbEl.getWidth() / this.minWidth;
27681 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27682 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27684 this.startScale = this.scale;
27686 while (this.getScaleLevel() < minScale){
27688 this.scale = this.scale + 1;
27690 if(!this.zoomable()){
27695 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27696 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27701 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27708 this.scale = this.startScale;
27710 this.onRotateFail();
27715 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27717 if(this.isDocument){
27718 this.setThumbBoxSize();
27719 this.setThumbBoxPosition();
27720 this.setCanvasPosition();
27725 this.fireEvent('rotate', this, 'right');
27728 onRotateFail : function()
27730 this.errorEl.show(true);
27734 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27739 this.previewEl.dom.innerHTML = '';
27741 var canvasEl = document.createElement("canvas");
27743 var contextEl = canvasEl.getContext("2d");
27745 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27746 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27747 var center = this.imageEl.OriginWidth / 2;
27749 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27750 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27751 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27752 center = this.imageEl.OriginHeight / 2;
27755 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27757 contextEl.translate(center, center);
27758 contextEl.rotate(this.rotate * Math.PI / 180);
27760 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27762 this.canvasEl = document.createElement("canvas");
27764 this.contextEl = this.canvasEl.getContext("2d");
27766 switch (this.rotate) {
27769 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27770 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27772 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27777 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27778 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27780 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27781 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);
27785 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27790 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27791 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27793 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27794 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);
27798 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);
27803 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27804 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27806 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27807 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27811 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);
27818 this.previewEl.appendChild(this.canvasEl);
27820 this.setCanvasPosition();
27825 if(!this.canvasLoaded){
27829 var imageCanvas = document.createElement("canvas");
27831 var imageContext = imageCanvas.getContext("2d");
27833 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27834 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27836 var center = imageCanvas.width / 2;
27838 imageContext.translate(center, center);
27840 imageContext.rotate(this.rotate * Math.PI / 180);
27842 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27844 var canvas = document.createElement("canvas");
27846 var context = canvas.getContext("2d");
27848 canvas.width = this.minWidth;
27849 canvas.height = this.minHeight;
27851 switch (this.rotate) {
27854 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27855 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27857 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27858 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27860 var targetWidth = this.minWidth - 2 * x;
27861 var targetHeight = this.minHeight - 2 * y;
27865 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27866 scale = targetWidth / width;
27869 if(x > 0 && y == 0){
27870 scale = targetHeight / height;
27873 if(x > 0 && y > 0){
27874 scale = targetWidth / width;
27876 if(width < height){
27877 scale = targetHeight / height;
27881 context.scale(scale, scale);
27883 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27884 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27886 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27887 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27889 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27894 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27895 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27897 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27898 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27900 var targetWidth = this.minWidth - 2 * x;
27901 var targetHeight = this.minHeight - 2 * y;
27905 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27906 scale = targetWidth / width;
27909 if(x > 0 && y == 0){
27910 scale = targetHeight / height;
27913 if(x > 0 && y > 0){
27914 scale = targetWidth / width;
27916 if(width < height){
27917 scale = targetHeight / height;
27921 context.scale(scale, scale);
27923 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27924 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27926 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27927 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27929 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27931 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27936 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27937 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27939 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27940 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27942 var targetWidth = this.minWidth - 2 * x;
27943 var targetHeight = this.minHeight - 2 * y;
27947 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27948 scale = targetWidth / width;
27951 if(x > 0 && y == 0){
27952 scale = targetHeight / height;
27955 if(x > 0 && y > 0){
27956 scale = targetWidth / width;
27958 if(width < height){
27959 scale = targetHeight / height;
27963 context.scale(scale, scale);
27965 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27966 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27968 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27969 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27971 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27972 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27974 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27979 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27980 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27982 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27983 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27985 var targetWidth = this.minWidth - 2 * x;
27986 var targetHeight = this.minHeight - 2 * y;
27990 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27991 scale = targetWidth / width;
27994 if(x > 0 && y == 0){
27995 scale = targetHeight / height;
27998 if(x > 0 && y > 0){
27999 scale = targetWidth / width;
28001 if(width < height){
28002 scale = targetHeight / height;
28006 context.scale(scale, scale);
28008 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28009 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28011 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28012 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28014 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28016 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28023 this.cropData = canvas.toDataURL(this.cropType);
28025 if(this.fireEvent('crop', this, this.cropData) !== false){
28026 this.process(this.file, this.cropData);
28033 setThumbBoxSize : function()
28037 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28038 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28039 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28041 this.minWidth = width;
28042 this.minHeight = height;
28044 if(this.rotate == 90 || this.rotate == 270){
28045 this.minWidth = height;
28046 this.minHeight = width;
28051 width = Math.ceil(this.minWidth * height / this.minHeight);
28053 if(this.minWidth > this.minHeight){
28055 height = Math.ceil(this.minHeight * width / this.minWidth);
28058 this.thumbEl.setStyle({
28059 width : width + 'px',
28060 height : height + 'px'
28067 setThumbBoxPosition : function()
28069 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28070 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28072 this.thumbEl.setLeft(x);
28073 this.thumbEl.setTop(y);
28077 baseRotateLevel : function()
28079 this.baseRotate = 1;
28082 typeof(this.exif) != 'undefined' &&
28083 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28084 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28086 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28089 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28093 baseScaleLevel : function()
28097 if(this.isDocument){
28099 if(this.baseRotate == 6 || this.baseRotate == 8){
28101 height = this.thumbEl.getHeight();
28102 this.baseScale = height / this.imageEl.OriginWidth;
28104 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28105 width = this.thumbEl.getWidth();
28106 this.baseScale = width / this.imageEl.OriginHeight;
28112 height = this.thumbEl.getHeight();
28113 this.baseScale = height / this.imageEl.OriginHeight;
28115 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28116 width = this.thumbEl.getWidth();
28117 this.baseScale = width / this.imageEl.OriginWidth;
28123 if(this.baseRotate == 6 || this.baseRotate == 8){
28125 width = this.thumbEl.getHeight();
28126 this.baseScale = width / this.imageEl.OriginHeight;
28128 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28129 height = this.thumbEl.getWidth();
28130 this.baseScale = height / this.imageEl.OriginHeight;
28133 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28134 height = this.thumbEl.getWidth();
28135 this.baseScale = height / this.imageEl.OriginHeight;
28137 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28138 width = this.thumbEl.getHeight();
28139 this.baseScale = width / this.imageEl.OriginWidth;
28146 width = this.thumbEl.getWidth();
28147 this.baseScale = width / this.imageEl.OriginWidth;
28149 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28150 height = this.thumbEl.getHeight();
28151 this.baseScale = height / this.imageEl.OriginHeight;
28154 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28156 height = this.thumbEl.getHeight();
28157 this.baseScale = height / this.imageEl.OriginHeight;
28159 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28160 width = this.thumbEl.getWidth();
28161 this.baseScale = width / this.imageEl.OriginWidth;
28169 getScaleLevel : function()
28171 return this.baseScale * Math.pow(1.1, this.scale);
28174 onTouchStart : function(e)
28176 if(!this.canvasLoaded){
28177 this.beforeSelectFile(e);
28181 var touches = e.browserEvent.touches;
28187 if(touches.length == 1){
28188 this.onMouseDown(e);
28192 if(touches.length != 2){
28198 for(var i = 0, finger; finger = touches[i]; i++){
28199 coords.push(finger.pageX, finger.pageY);
28202 var x = Math.pow(coords[0] - coords[2], 2);
28203 var y = Math.pow(coords[1] - coords[3], 2);
28205 this.startDistance = Math.sqrt(x + y);
28207 this.startScale = this.scale;
28209 this.pinching = true;
28210 this.dragable = false;
28214 onTouchMove : function(e)
28216 if(!this.pinching && !this.dragable){
28220 var touches = e.browserEvent.touches;
28227 this.onMouseMove(e);
28233 for(var i = 0, finger; finger = touches[i]; i++){
28234 coords.push(finger.pageX, finger.pageY);
28237 var x = Math.pow(coords[0] - coords[2], 2);
28238 var y = Math.pow(coords[1] - coords[3], 2);
28240 this.endDistance = Math.sqrt(x + y);
28242 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28244 if(!this.zoomable()){
28245 this.scale = this.startScale;
28253 onTouchEnd : function(e)
28255 this.pinching = false;
28256 this.dragable = false;
28260 process : function(file, crop)
28263 this.maskEl.mask(this.loadingText);
28266 this.xhr = new XMLHttpRequest();
28268 file.xhr = this.xhr;
28270 this.xhr.open(this.method, this.url, true);
28273 "Accept": "application/json",
28274 "Cache-Control": "no-cache",
28275 "X-Requested-With": "XMLHttpRequest"
28278 for (var headerName in headers) {
28279 var headerValue = headers[headerName];
28281 this.xhr.setRequestHeader(headerName, headerValue);
28287 this.xhr.onload = function()
28289 _this.xhrOnLoad(_this.xhr);
28292 this.xhr.onerror = function()
28294 _this.xhrOnError(_this.xhr);
28297 var formData = new FormData();
28299 formData.append('returnHTML', 'NO');
28302 formData.append('crop', crop);
28305 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28306 formData.append(this.paramName, file, file.name);
28309 if(typeof(file.filename) != 'undefined'){
28310 formData.append('filename', file.filename);
28313 if(typeof(file.mimetype) != 'undefined'){
28314 formData.append('mimetype', file.mimetype);
28317 if(this.fireEvent('arrange', this, formData) != false){
28318 this.xhr.send(formData);
28322 xhrOnLoad : function(xhr)
28325 this.maskEl.unmask();
28328 if (xhr.readyState !== 4) {
28329 this.fireEvent('exception', this, xhr);
28333 var response = Roo.decode(xhr.responseText);
28335 if(!response.success){
28336 this.fireEvent('exception', this, xhr);
28340 var response = Roo.decode(xhr.responseText);
28342 this.fireEvent('upload', this, response);
28346 xhrOnError : function()
28349 this.maskEl.unmask();
28352 Roo.log('xhr on error');
28354 var response = Roo.decode(xhr.responseText);
28360 prepare : function(file)
28363 this.maskEl.mask(this.loadingText);
28369 if(typeof(file) === 'string'){
28370 this.loadCanvas(file);
28374 if(!file || !this.urlAPI){
28379 this.cropType = file.type;
28383 if(this.fireEvent('prepare', this, this.file) != false){
28385 var reader = new FileReader();
28387 reader.onload = function (e) {
28388 if (e.target.error) {
28389 Roo.log(e.target.error);
28393 var buffer = e.target.result,
28394 dataView = new DataView(buffer),
28396 maxOffset = dataView.byteLength - 4,
28400 if (dataView.getUint16(0) === 0xffd8) {
28401 while (offset < maxOffset) {
28402 markerBytes = dataView.getUint16(offset);
28404 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28405 markerLength = dataView.getUint16(offset + 2) + 2;
28406 if (offset + markerLength > dataView.byteLength) {
28407 Roo.log('Invalid meta data: Invalid segment size.');
28411 if(markerBytes == 0xffe1){
28412 _this.parseExifData(
28419 offset += markerLength;
28429 var url = _this.urlAPI.createObjectURL(_this.file);
28431 _this.loadCanvas(url);
28436 reader.readAsArrayBuffer(this.file);
28442 parseExifData : function(dataView, offset, length)
28444 var tiffOffset = offset + 10,
28448 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28449 // No Exif data, might be XMP data instead
28453 // Check for the ASCII code for "Exif" (0x45786966):
28454 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28455 // No Exif data, might be XMP data instead
28458 if (tiffOffset + 8 > dataView.byteLength) {
28459 Roo.log('Invalid Exif data: Invalid segment size.');
28462 // Check for the two null bytes:
28463 if (dataView.getUint16(offset + 8) !== 0x0000) {
28464 Roo.log('Invalid Exif data: Missing byte alignment offset.');
28467 // Check the byte alignment:
28468 switch (dataView.getUint16(tiffOffset)) {
28470 littleEndian = true;
28473 littleEndian = false;
28476 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28479 // Check for the TIFF tag marker (0x002A):
28480 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28481 Roo.log('Invalid Exif data: Missing TIFF marker.');
28484 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28485 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28487 this.parseExifTags(
28490 tiffOffset + dirOffset,
28495 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28500 if (dirOffset + 6 > dataView.byteLength) {
28501 Roo.log('Invalid Exif data: Invalid directory offset.');
28504 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28505 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28506 if (dirEndOffset + 4 > dataView.byteLength) {
28507 Roo.log('Invalid Exif data: Invalid directory size.');
28510 for (i = 0; i < tagsNumber; i += 1) {
28514 dirOffset + 2 + 12 * i, // tag offset
28518 // Return the offset to the next directory:
28519 return dataView.getUint32(dirEndOffset, littleEndian);
28522 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
28524 var tag = dataView.getUint16(offset, littleEndian);
28526 this.exif[tag] = this.getExifValue(
28530 dataView.getUint16(offset + 2, littleEndian), // tag type
28531 dataView.getUint32(offset + 4, littleEndian), // tag length
28536 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28538 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28547 Roo.log('Invalid Exif data: Invalid tag type.');
28551 tagSize = tagType.size * length;
28552 // Determine if the value is contained in the dataOffset bytes,
28553 // or if the value at the dataOffset is a pointer to the actual data:
28554 dataOffset = tagSize > 4 ?
28555 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28556 if (dataOffset + tagSize > dataView.byteLength) {
28557 Roo.log('Invalid Exif data: Invalid data offset.');
28560 if (length === 1) {
28561 return tagType.getValue(dataView, dataOffset, littleEndian);
28564 for (i = 0; i < length; i += 1) {
28565 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28568 if (tagType.ascii) {
28570 // Concatenate the chars:
28571 for (i = 0; i < values.length; i += 1) {
28573 // Ignore the terminating NULL byte(s):
28574 if (c === '\u0000') {
28586 Roo.apply(Roo.bootstrap.UploadCropbox, {
28588 'Orientation': 0x0112
28592 1: 0, //'top-left',
28594 3: 180, //'bottom-right',
28595 // 4: 'bottom-left',
28597 6: 90, //'right-top',
28598 // 7: 'right-bottom',
28599 8: 270 //'left-bottom'
28603 // byte, 8-bit unsigned int:
28605 getValue: function (dataView, dataOffset) {
28606 return dataView.getUint8(dataOffset);
28610 // ascii, 8-bit byte:
28612 getValue: function (dataView, dataOffset) {
28613 return String.fromCharCode(dataView.getUint8(dataOffset));
28618 // short, 16 bit int:
28620 getValue: function (dataView, dataOffset, littleEndian) {
28621 return dataView.getUint16(dataOffset, littleEndian);
28625 // long, 32 bit int:
28627 getValue: function (dataView, dataOffset, littleEndian) {
28628 return dataView.getUint32(dataOffset, littleEndian);
28632 // rational = two long values, first is numerator, second is denominator:
28634 getValue: function (dataView, dataOffset, littleEndian) {
28635 return dataView.getUint32(dataOffset, littleEndian) /
28636 dataView.getUint32(dataOffset + 4, littleEndian);
28640 // slong, 32 bit signed int:
28642 getValue: function (dataView, dataOffset, littleEndian) {
28643 return dataView.getInt32(dataOffset, littleEndian);
28647 // srational, two slongs, first is numerator, second is denominator:
28649 getValue: function (dataView, dataOffset, littleEndian) {
28650 return dataView.getInt32(dataOffset, littleEndian) /
28651 dataView.getInt32(dataOffset + 4, littleEndian);
28661 cls : 'btn-group roo-upload-cropbox-rotate-left',
28662 action : 'rotate-left',
28666 cls : 'btn btn-default',
28667 html : '<i class="fa fa-undo"></i>'
28673 cls : 'btn-group roo-upload-cropbox-picture',
28674 action : 'picture',
28678 cls : 'btn btn-default',
28679 html : '<i class="fa fa-picture-o"></i>'
28685 cls : 'btn-group roo-upload-cropbox-rotate-right',
28686 action : 'rotate-right',
28690 cls : 'btn btn-default',
28691 html : '<i class="fa fa-repeat"></i>'
28699 cls : 'btn-group roo-upload-cropbox-rotate-left',
28700 action : 'rotate-left',
28704 cls : 'btn btn-default',
28705 html : '<i class="fa fa-undo"></i>'
28711 cls : 'btn-group roo-upload-cropbox-download',
28712 action : 'download',
28716 cls : 'btn btn-default',
28717 html : '<i class="fa fa-download"></i>'
28723 cls : 'btn-group roo-upload-cropbox-crop',
28728 cls : 'btn btn-default',
28729 html : '<i class="fa fa-crop"></i>'
28735 cls : 'btn-group roo-upload-cropbox-trash',
28740 cls : 'btn btn-default',
28741 html : '<i class="fa fa-trash"></i>'
28747 cls : 'btn-group roo-upload-cropbox-rotate-right',
28748 action : 'rotate-right',
28752 cls : 'btn btn-default',
28753 html : '<i class="fa fa-repeat"></i>'
28761 cls : 'btn-group roo-upload-cropbox-rotate-left',
28762 action : 'rotate-left',
28766 cls : 'btn btn-default',
28767 html : '<i class="fa fa-undo"></i>'
28773 cls : 'btn-group roo-upload-cropbox-rotate-right',
28774 action : 'rotate-right',
28778 cls : 'btn btn-default',
28779 html : '<i class="fa fa-repeat"></i>'
28792 * @class Roo.bootstrap.DocumentManager
28793 * @extends Roo.bootstrap.Component
28794 * Bootstrap DocumentManager class
28795 * @cfg {String} paramName default 'imageUpload'
28796 * @cfg {String} toolTipName default 'filename'
28797 * @cfg {String} method default POST
28798 * @cfg {String} url action url
28799 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28800 * @cfg {Boolean} multiple multiple upload default true
28801 * @cfg {Number} thumbSize default 300
28802 * @cfg {String} fieldLabel
28803 * @cfg {Number} labelWidth default 4
28804 * @cfg {String} labelAlign (left|top) default left
28805 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28806 * @cfg {Number} labellg set the width of label (1-12)
28807 * @cfg {Number} labelmd set the width of label (1-12)
28808 * @cfg {Number} labelsm set the width of label (1-12)
28809 * @cfg {Number} labelxs set the width of label (1-12)
28812 * Create a new DocumentManager
28813 * @param {Object} config The config object
28816 Roo.bootstrap.DocumentManager = function(config){
28817 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28820 this.delegates = [];
28825 * Fire when initial the DocumentManager
28826 * @param {Roo.bootstrap.DocumentManager} this
28831 * inspect selected file
28832 * @param {Roo.bootstrap.DocumentManager} this
28833 * @param {File} file
28838 * Fire when xhr load exception
28839 * @param {Roo.bootstrap.DocumentManager} this
28840 * @param {XMLHttpRequest} xhr
28842 "exception" : true,
28844 * @event afterupload
28845 * Fire when xhr load exception
28846 * @param {Roo.bootstrap.DocumentManager} this
28847 * @param {XMLHttpRequest} xhr
28849 "afterupload" : true,
28852 * prepare the form data
28853 * @param {Roo.bootstrap.DocumentManager} this
28854 * @param {Object} formData
28859 * Fire when remove the file
28860 * @param {Roo.bootstrap.DocumentManager} this
28861 * @param {Object} file
28866 * Fire after refresh the file
28867 * @param {Roo.bootstrap.DocumentManager} this
28872 * Fire after click the image
28873 * @param {Roo.bootstrap.DocumentManager} this
28874 * @param {Object} file
28879 * Fire when upload a image and editable set to true
28880 * @param {Roo.bootstrap.DocumentManager} this
28881 * @param {Object} file
28885 * @event beforeselectfile
28886 * Fire before select file
28887 * @param {Roo.bootstrap.DocumentManager} this
28889 "beforeselectfile" : true,
28892 * Fire before process file
28893 * @param {Roo.bootstrap.DocumentManager} this
28894 * @param {Object} file
28898 * @event previewrendered
28899 * Fire when preview rendered
28900 * @param {Roo.bootstrap.DocumentManager} this
28901 * @param {Object} file
28903 "previewrendered" : true,
28906 "previewResize" : true
28911 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
28920 paramName : 'imageUpload',
28921 toolTipName : 'filename',
28924 labelAlign : 'left',
28934 getAutoCreate : function()
28936 var managerWidget = {
28938 cls : 'roo-document-manager',
28942 cls : 'roo-document-manager-selector',
28947 cls : 'roo-document-manager-uploader',
28951 cls : 'roo-document-manager-upload-btn',
28952 html : '<i class="fa fa-plus"></i>'
28963 cls : 'column col-md-12',
28968 if(this.fieldLabel.length){
28973 cls : 'column col-md-12',
28974 html : this.fieldLabel
28978 cls : 'column col-md-12',
28983 if(this.labelAlign == 'left'){
28988 html : this.fieldLabel
28997 if(this.labelWidth > 12){
28998 content[0].style = "width: " + this.labelWidth + 'px';
29001 if(this.labelWidth < 13 && this.labelmd == 0){
29002 this.labelmd = this.labelWidth;
29005 if(this.labellg > 0){
29006 content[0].cls += ' col-lg-' + this.labellg;
29007 content[1].cls += ' col-lg-' + (12 - this.labellg);
29010 if(this.labelmd > 0){
29011 content[0].cls += ' col-md-' + this.labelmd;
29012 content[1].cls += ' col-md-' + (12 - this.labelmd);
29015 if(this.labelsm > 0){
29016 content[0].cls += ' col-sm-' + this.labelsm;
29017 content[1].cls += ' col-sm-' + (12 - this.labelsm);
29020 if(this.labelxs > 0){
29021 content[0].cls += ' col-xs-' + this.labelxs;
29022 content[1].cls += ' col-xs-' + (12 - this.labelxs);
29030 cls : 'row clearfix',
29038 initEvents : function()
29040 this.managerEl = this.el.select('.roo-document-manager', true).first();
29041 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29043 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29044 this.selectorEl.hide();
29047 this.selectorEl.attr('multiple', 'multiple');
29050 this.selectorEl.on('change', this.onFileSelected, this);
29052 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29053 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29055 this.uploader.on('click', this.onUploaderClick, this);
29057 this.renderProgressDialog();
29061 window.addEventListener("resize", function() { _this.refresh(); } );
29063 this.fireEvent('initial', this);
29066 renderProgressDialog : function()
29070 this.progressDialog = new Roo.bootstrap.Modal({
29071 cls : 'roo-document-manager-progress-dialog',
29072 allow_close : false,
29082 btnclick : function() {
29083 _this.uploadCancel();
29089 this.progressDialog.render(Roo.get(document.body));
29091 this.progress = new Roo.bootstrap.Progress({
29092 cls : 'roo-document-manager-progress',
29097 this.progress.render(this.progressDialog.getChildContainer());
29099 this.progressBar = new Roo.bootstrap.ProgressBar({
29100 cls : 'roo-document-manager-progress-bar',
29103 aria_valuemax : 12,
29107 this.progressBar.render(this.progress.getChildContainer());
29110 onUploaderClick : function(e)
29112 e.preventDefault();
29114 if(this.fireEvent('beforeselectfile', this) != false){
29115 this.selectorEl.dom.click();
29120 onFileSelected : function(e)
29122 e.preventDefault();
29124 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29128 Roo.each(this.selectorEl.dom.files, function(file){
29129 if(this.fireEvent('inspect', this, file) != false){
29130 this.files.push(file);
29140 this.selectorEl.dom.value = '';
29142 if(!this.files || !this.files.length){
29146 if(this.boxes > 0 && this.files.length > this.boxes){
29147 this.files = this.files.slice(0, this.boxes);
29150 this.uploader.show();
29152 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29153 this.uploader.hide();
29162 Roo.each(this.files, function(file){
29164 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29165 var f = this.renderPreview(file);
29170 if(file.type.indexOf('image') != -1){
29171 this.delegates.push(
29173 _this.process(file);
29174 }).createDelegate(this)
29182 _this.process(file);
29183 }).createDelegate(this)
29188 this.files = files;
29190 this.delegates = this.delegates.concat(docs);
29192 if(!this.delegates.length){
29197 this.progressBar.aria_valuemax = this.delegates.length;
29204 arrange : function()
29206 if(!this.delegates.length){
29207 this.progressDialog.hide();
29212 var delegate = this.delegates.shift();
29214 this.progressDialog.show();
29216 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29218 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29223 refresh : function()
29225 this.uploader.show();
29227 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29228 this.uploader.hide();
29231 Roo.isTouch ? this.closable(false) : this.closable(true);
29233 this.fireEvent('refresh', this);
29236 onRemove : function(e, el, o)
29238 e.preventDefault();
29240 this.fireEvent('remove', this, o);
29244 remove : function(o)
29248 Roo.each(this.files, function(file){
29249 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29258 this.files = files;
29265 Roo.each(this.files, function(file){
29270 file.target.remove();
29279 onClick : function(e, el, o)
29281 e.preventDefault();
29283 this.fireEvent('click', this, o);
29287 closable : function(closable)
29289 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29291 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29303 xhrOnLoad : function(xhr)
29305 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29309 if (xhr.readyState !== 4) {
29311 this.fireEvent('exception', this, xhr);
29315 var response = Roo.decode(xhr.responseText);
29317 if(!response.success){
29319 this.fireEvent('exception', this, xhr);
29323 var file = this.renderPreview(response.data);
29325 this.files.push(file);
29329 this.fireEvent('afterupload', this, xhr);
29333 xhrOnError : function(xhr)
29335 Roo.log('xhr on error');
29337 var response = Roo.decode(xhr.responseText);
29344 process : function(file)
29346 if(this.fireEvent('process', this, file) !== false){
29347 if(this.editable && file.type.indexOf('image') != -1){
29348 this.fireEvent('edit', this, file);
29352 this.uploadStart(file, false);
29359 uploadStart : function(file, crop)
29361 this.xhr = new XMLHttpRequest();
29363 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29368 file.xhr = this.xhr;
29370 this.managerEl.createChild({
29372 cls : 'roo-document-manager-loading',
29376 tooltip : file.name,
29377 cls : 'roo-document-manager-thumb',
29378 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29384 this.xhr.open(this.method, this.url, true);
29387 "Accept": "application/json",
29388 "Cache-Control": "no-cache",
29389 "X-Requested-With": "XMLHttpRequest"
29392 for (var headerName in headers) {
29393 var headerValue = headers[headerName];
29395 this.xhr.setRequestHeader(headerName, headerValue);
29401 this.xhr.onload = function()
29403 _this.xhrOnLoad(_this.xhr);
29406 this.xhr.onerror = function()
29408 _this.xhrOnError(_this.xhr);
29411 var formData = new FormData();
29413 formData.append('returnHTML', 'NO');
29416 formData.append('crop', crop);
29419 formData.append(this.paramName, file, file.name);
29426 if(this.fireEvent('prepare', this, formData, options) != false){
29428 if(options.manually){
29432 this.xhr.send(formData);
29436 this.uploadCancel();
29439 uploadCancel : function()
29445 this.delegates = [];
29447 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29454 renderPreview : function(file)
29456 if(typeof(file.target) != 'undefined' && file.target){
29460 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29462 var previewEl = this.managerEl.createChild({
29464 cls : 'roo-document-manager-preview',
29468 tooltip : file[this.toolTipName],
29469 cls : 'roo-document-manager-thumb',
29470 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29475 html : '<i class="fa fa-times-circle"></i>'
29480 var close = previewEl.select('button.close', true).first();
29482 close.on('click', this.onRemove, this, file);
29484 file.target = previewEl;
29486 var image = previewEl.select('img', true).first();
29490 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29492 image.on('click', this.onClick, this, file);
29494 this.fireEvent('previewrendered', this, file);
29500 onPreviewLoad : function(file, image)
29502 if(typeof(file.target) == 'undefined' || !file.target){
29506 var width = image.dom.naturalWidth || image.dom.width;
29507 var height = image.dom.naturalHeight || image.dom.height;
29509 if(!this.previewResize) {
29513 if(width > height){
29514 file.target.addClass('wide');
29518 file.target.addClass('tall');
29523 uploadFromSource : function(file, crop)
29525 this.xhr = new XMLHttpRequest();
29527 this.managerEl.createChild({
29529 cls : 'roo-document-manager-loading',
29533 tooltip : file.name,
29534 cls : 'roo-document-manager-thumb',
29535 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29541 this.xhr.open(this.method, this.url, true);
29544 "Accept": "application/json",
29545 "Cache-Control": "no-cache",
29546 "X-Requested-With": "XMLHttpRequest"
29549 for (var headerName in headers) {
29550 var headerValue = headers[headerName];
29552 this.xhr.setRequestHeader(headerName, headerValue);
29558 this.xhr.onload = function()
29560 _this.xhrOnLoad(_this.xhr);
29563 this.xhr.onerror = function()
29565 _this.xhrOnError(_this.xhr);
29568 var formData = new FormData();
29570 formData.append('returnHTML', 'NO');
29572 formData.append('crop', crop);
29574 if(typeof(file.filename) != 'undefined'){
29575 formData.append('filename', file.filename);
29578 if(typeof(file.mimetype) != 'undefined'){
29579 formData.append('mimetype', file.mimetype);
29584 if(this.fireEvent('prepare', this, formData) != false){
29585 this.xhr.send(formData);
29595 * @class Roo.bootstrap.DocumentViewer
29596 * @extends Roo.bootstrap.Component
29597 * Bootstrap DocumentViewer class
29598 * @cfg {Boolean} showDownload (true|false) show download button (default true)
29599 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29602 * Create a new DocumentViewer
29603 * @param {Object} config The config object
29606 Roo.bootstrap.DocumentViewer = function(config){
29607 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29612 * Fire after initEvent
29613 * @param {Roo.bootstrap.DocumentViewer} this
29619 * @param {Roo.bootstrap.DocumentViewer} this
29624 * Fire after download button
29625 * @param {Roo.bootstrap.DocumentViewer} this
29630 * Fire after trash button
29631 * @param {Roo.bootstrap.DocumentViewer} this
29638 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
29640 showDownload : true,
29644 getAutoCreate : function()
29648 cls : 'roo-document-viewer',
29652 cls : 'roo-document-viewer-body',
29656 cls : 'roo-document-viewer-thumb',
29660 cls : 'roo-document-viewer-image'
29668 cls : 'roo-document-viewer-footer',
29671 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29675 cls : 'btn-group roo-document-viewer-download',
29679 cls : 'btn btn-default',
29680 html : '<i class="fa fa-download"></i>'
29686 cls : 'btn-group roo-document-viewer-trash',
29690 cls : 'btn btn-default',
29691 html : '<i class="fa fa-trash"></i>'
29704 initEvents : function()
29706 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29707 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29709 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29710 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29712 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29713 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29715 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29716 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29718 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29719 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29721 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29722 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29724 this.bodyEl.on('click', this.onClick, this);
29725 this.downloadBtn.on('click', this.onDownload, this);
29726 this.trashBtn.on('click', this.onTrash, this);
29728 this.downloadBtn.hide();
29729 this.trashBtn.hide();
29731 if(this.showDownload){
29732 this.downloadBtn.show();
29735 if(this.showTrash){
29736 this.trashBtn.show();
29739 if(!this.showDownload && !this.showTrash) {
29740 this.footerEl.hide();
29745 initial : function()
29747 this.fireEvent('initial', this);
29751 onClick : function(e)
29753 e.preventDefault();
29755 this.fireEvent('click', this);
29758 onDownload : function(e)
29760 e.preventDefault();
29762 this.fireEvent('download', this);
29765 onTrash : function(e)
29767 e.preventDefault();
29769 this.fireEvent('trash', this);
29781 * @class Roo.bootstrap.NavProgressBar
29782 * @extends Roo.bootstrap.Component
29783 * Bootstrap NavProgressBar class
29786 * Create a new nav progress bar
29787 * @param {Object} config The config object
29790 Roo.bootstrap.NavProgressBar = function(config){
29791 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29793 this.bullets = this.bullets || [];
29795 // Roo.bootstrap.NavProgressBar.register(this);
29799 * Fires when the active item changes
29800 * @param {Roo.bootstrap.NavProgressBar} this
29801 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29802 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
29809 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
29814 getAutoCreate : function()
29816 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29820 cls : 'roo-navigation-bar-group',
29824 cls : 'roo-navigation-top-bar'
29828 cls : 'roo-navigation-bullets-bar',
29832 cls : 'roo-navigation-bar'
29839 cls : 'roo-navigation-bottom-bar'
29849 initEvents: function()
29854 onRender : function(ct, position)
29856 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29858 if(this.bullets.length){
29859 Roo.each(this.bullets, function(b){
29868 addItem : function(cfg)
29870 var item = new Roo.bootstrap.NavProgressItem(cfg);
29872 item.parentId = this.id;
29873 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29876 var top = new Roo.bootstrap.Element({
29878 cls : 'roo-navigation-bar-text'
29881 var bottom = new Roo.bootstrap.Element({
29883 cls : 'roo-navigation-bar-text'
29886 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29887 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29889 var topText = new Roo.bootstrap.Element({
29891 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29894 var bottomText = new Roo.bootstrap.Element({
29896 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29899 topText.onRender(top.el, null);
29900 bottomText.onRender(bottom.el, null);
29903 item.bottomEl = bottom;
29906 this.barItems.push(item);
29911 getActive : function()
29913 var active = false;
29915 Roo.each(this.barItems, function(v){
29917 if (!v.isActive()) {
29929 setActiveItem : function(item)
29933 Roo.each(this.barItems, function(v){
29934 if (v.rid == item.rid) {
29938 if (v.isActive()) {
29939 v.setActive(false);
29944 item.setActive(true);
29946 this.fireEvent('changed', this, item, prev);
29949 getBarItem: function(rid)
29953 Roo.each(this.barItems, function(e) {
29954 if (e.rid != rid) {
29965 indexOfItem : function(item)
29969 Roo.each(this.barItems, function(v, i){
29971 if (v.rid != item.rid) {
29982 setActiveNext : function()
29984 var i = this.indexOfItem(this.getActive());
29986 if (i > this.barItems.length) {
29990 this.setActiveItem(this.barItems[i+1]);
29993 setActivePrev : function()
29995 var i = this.indexOfItem(this.getActive());
30001 this.setActiveItem(this.barItems[i-1]);
30004 format : function()
30006 if(!this.barItems.length){
30010 var width = 100 / this.barItems.length;
30012 Roo.each(this.barItems, function(i){
30013 i.el.setStyle('width', width + '%');
30014 i.topEl.el.setStyle('width', width + '%');
30015 i.bottomEl.el.setStyle('width', width + '%');
30024 * Nav Progress Item
30029 * @class Roo.bootstrap.NavProgressItem
30030 * @extends Roo.bootstrap.Component
30031 * Bootstrap NavProgressItem class
30032 * @cfg {String} rid the reference id
30033 * @cfg {Boolean} active (true|false) Is item active default false
30034 * @cfg {Boolean} disabled (true|false) Is item active default false
30035 * @cfg {String} html
30036 * @cfg {String} position (top|bottom) text position default bottom
30037 * @cfg {String} icon show icon instead of number
30040 * Create a new NavProgressItem
30041 * @param {Object} config The config object
30043 Roo.bootstrap.NavProgressItem = function(config){
30044 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30049 * The raw click event for the entire grid.
30050 * @param {Roo.bootstrap.NavProgressItem} this
30051 * @param {Roo.EventObject} e
30058 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
30064 position : 'bottom',
30067 getAutoCreate : function()
30069 var iconCls = 'roo-navigation-bar-item-icon';
30071 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30075 cls: 'roo-navigation-bar-item',
30085 cfg.cls += ' active';
30088 cfg.cls += ' disabled';
30094 disable : function()
30096 this.setDisabled(true);
30099 enable : function()
30101 this.setDisabled(false);
30104 initEvents: function()
30106 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30108 this.iconEl.on('click', this.onClick, this);
30111 onClick : function(e)
30113 e.preventDefault();
30119 if(this.fireEvent('click', this, e) === false){
30123 this.parent().setActiveItem(this);
30126 isActive: function ()
30128 return this.active;
30131 setActive : function(state)
30133 if(this.active == state){
30137 this.active = state;
30140 this.el.addClass('active');
30144 this.el.removeClass('active');
30149 setDisabled : function(state)
30151 if(this.disabled == state){
30155 this.disabled = state;
30158 this.el.addClass('disabled');
30162 this.el.removeClass('disabled');
30165 tooltipEl : function()
30167 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30180 * @class Roo.bootstrap.FieldLabel
30181 * @extends Roo.bootstrap.Component
30182 * Bootstrap FieldLabel class
30183 * @cfg {String} html contents of the element
30184 * @cfg {String} tag tag of the element default label
30185 * @cfg {String} cls class of the element
30186 * @cfg {String} target label target
30187 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30188 * @cfg {String} invalidClass default "text-warning"
30189 * @cfg {String} validClass default "text-success"
30190 * @cfg {String} iconTooltip default "This field is required"
30191 * @cfg {String} indicatorpos (left|right) default left
30194 * Create a new FieldLabel
30195 * @param {Object} config The config object
30198 Roo.bootstrap.FieldLabel = function(config){
30199 Roo.bootstrap.Element.superclass.constructor.call(this, config);
30204 * Fires after the field has been marked as invalid.
30205 * @param {Roo.form.FieldLabel} this
30206 * @param {String} msg The validation message
30211 * Fires after the field has been validated with no errors.
30212 * @param {Roo.form.FieldLabel} this
30218 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
30225 invalidClass : 'has-warning',
30226 validClass : 'has-success',
30227 iconTooltip : 'This field is required',
30228 indicatorpos : 'left',
30230 getAutoCreate : function(){
30233 if (!this.allowBlank) {
30239 cls : 'roo-bootstrap-field-label ' + this.cls,
30244 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30245 tooltip : this.iconTooltip
30254 if(this.indicatorpos == 'right'){
30257 cls : 'roo-bootstrap-field-label ' + this.cls,
30266 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30267 tooltip : this.iconTooltip
30276 initEvents: function()
30278 Roo.bootstrap.Element.superclass.initEvents.call(this);
30280 this.indicator = this.indicatorEl();
30282 if(this.indicator){
30283 this.indicator.removeClass('visible');
30284 this.indicator.addClass('invisible');
30287 Roo.bootstrap.FieldLabel.register(this);
30290 indicatorEl : function()
30292 var indicator = this.el.select('i.roo-required-indicator',true).first();
30303 * Mark this field as valid
30305 markValid : function()
30307 if(this.indicator){
30308 this.indicator.removeClass('visible');
30309 this.indicator.addClass('invisible');
30312 this.el.removeClass(this.invalidClass);
30314 this.el.addClass(this.validClass);
30316 this.fireEvent('valid', this);
30320 * Mark this field as invalid
30321 * @param {String} msg The validation message
30323 markInvalid : function(msg)
30325 if(this.indicator){
30326 this.indicator.removeClass('invisible');
30327 this.indicator.addClass('visible');
30330 this.el.removeClass(this.validClass);
30332 this.el.addClass(this.invalidClass);
30334 this.fireEvent('invalid', this, msg);
30340 Roo.apply(Roo.bootstrap.FieldLabel, {
30345 * register a FieldLabel Group
30346 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30348 register : function(label)
30350 if(this.groups.hasOwnProperty(label.target)){
30354 this.groups[label.target] = label;
30358 * fetch a FieldLabel Group based on the target
30359 * @param {string} target
30360 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30362 get: function(target) {
30363 if (typeof(this.groups[target]) == 'undefined') {
30367 return this.groups[target] ;
30376 * page DateSplitField.
30382 * @class Roo.bootstrap.DateSplitField
30383 * @extends Roo.bootstrap.Component
30384 * Bootstrap DateSplitField class
30385 * @cfg {string} fieldLabel - the label associated
30386 * @cfg {Number} labelWidth set the width of label (0-12)
30387 * @cfg {String} labelAlign (top|left)
30388 * @cfg {Boolean} dayAllowBlank (true|false) default false
30389 * @cfg {Boolean} monthAllowBlank (true|false) default false
30390 * @cfg {Boolean} yearAllowBlank (true|false) default false
30391 * @cfg {string} dayPlaceholder
30392 * @cfg {string} monthPlaceholder
30393 * @cfg {string} yearPlaceholder
30394 * @cfg {string} dayFormat default 'd'
30395 * @cfg {string} monthFormat default 'm'
30396 * @cfg {string} yearFormat default 'Y'
30397 * @cfg {Number} labellg set the width of label (1-12)
30398 * @cfg {Number} labelmd set the width of label (1-12)
30399 * @cfg {Number} labelsm set the width of label (1-12)
30400 * @cfg {Number} labelxs set the width of label (1-12)
30404 * Create a new DateSplitField
30405 * @param {Object} config The config object
30408 Roo.bootstrap.DateSplitField = function(config){
30409 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30415 * getting the data of years
30416 * @param {Roo.bootstrap.DateSplitField} this
30417 * @param {Object} years
30422 * getting the data of days
30423 * @param {Roo.bootstrap.DateSplitField} this
30424 * @param {Object} days
30429 * Fires after the field has been marked as invalid.
30430 * @param {Roo.form.Field} this
30431 * @param {String} msg The validation message
30436 * Fires after the field has been validated with no errors.
30437 * @param {Roo.form.Field} this
30443 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
30446 labelAlign : 'top',
30448 dayAllowBlank : false,
30449 monthAllowBlank : false,
30450 yearAllowBlank : false,
30451 dayPlaceholder : '',
30452 monthPlaceholder : '',
30453 yearPlaceholder : '',
30457 isFormField : true,
30463 getAutoCreate : function()
30467 cls : 'row roo-date-split-field-group',
30472 cls : 'form-hidden-field roo-date-split-field-group-value',
30478 var labelCls = 'col-md-12';
30479 var contentCls = 'col-md-4';
30481 if(this.fieldLabel){
30485 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30489 html : this.fieldLabel
30494 if(this.labelAlign == 'left'){
30496 if(this.labelWidth > 12){
30497 label.style = "width: " + this.labelWidth + 'px';
30500 if(this.labelWidth < 13 && this.labelmd == 0){
30501 this.labelmd = this.labelWidth;
30504 if(this.labellg > 0){
30505 labelCls = ' col-lg-' + this.labellg;
30506 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30509 if(this.labelmd > 0){
30510 labelCls = ' col-md-' + this.labelmd;
30511 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30514 if(this.labelsm > 0){
30515 labelCls = ' col-sm-' + this.labelsm;
30516 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30519 if(this.labelxs > 0){
30520 labelCls = ' col-xs-' + this.labelxs;
30521 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30525 label.cls += ' ' + labelCls;
30527 cfg.cn.push(label);
30530 Roo.each(['day', 'month', 'year'], function(t){
30533 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30540 inputEl: function ()
30542 return this.el.select('.roo-date-split-field-group-value', true).first();
30545 onRender : function(ct, position)
30549 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30551 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30553 this.dayField = new Roo.bootstrap.ComboBox({
30554 allowBlank : this.dayAllowBlank,
30555 alwaysQuery : true,
30556 displayField : 'value',
30559 forceSelection : true,
30561 placeholder : this.dayPlaceholder,
30562 selectOnFocus : true,
30563 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30564 triggerAction : 'all',
30566 valueField : 'value',
30567 store : new Roo.data.SimpleStore({
30568 data : (function() {
30570 _this.fireEvent('days', _this, days);
30573 fields : [ 'value' ]
30576 select : function (_self, record, index)
30578 _this.setValue(_this.getValue());
30583 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30585 this.monthField = new Roo.bootstrap.MonthField({
30586 after : '<i class=\"fa fa-calendar\"></i>',
30587 allowBlank : this.monthAllowBlank,
30588 placeholder : this.monthPlaceholder,
30591 render : function (_self)
30593 this.el.select('span.input-group-addon', true).first().on('click', function(e){
30594 e.preventDefault();
30598 select : function (_self, oldvalue, newvalue)
30600 _this.setValue(_this.getValue());
30605 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30607 this.yearField = new Roo.bootstrap.ComboBox({
30608 allowBlank : this.yearAllowBlank,
30609 alwaysQuery : true,
30610 displayField : 'value',
30613 forceSelection : true,
30615 placeholder : this.yearPlaceholder,
30616 selectOnFocus : true,
30617 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30618 triggerAction : 'all',
30620 valueField : 'value',
30621 store : new Roo.data.SimpleStore({
30622 data : (function() {
30624 _this.fireEvent('years', _this, years);
30627 fields : [ 'value' ]
30630 select : function (_self, record, index)
30632 _this.setValue(_this.getValue());
30637 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30640 setValue : function(v, format)
30642 this.inputEl.dom.value = v;
30644 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30646 var d = Date.parseDate(v, f);
30653 this.setDay(d.format(this.dayFormat));
30654 this.setMonth(d.format(this.monthFormat));
30655 this.setYear(d.format(this.yearFormat));
30662 setDay : function(v)
30664 this.dayField.setValue(v);
30665 this.inputEl.dom.value = this.getValue();
30670 setMonth : function(v)
30672 this.monthField.setValue(v, true);
30673 this.inputEl.dom.value = this.getValue();
30678 setYear : function(v)
30680 this.yearField.setValue(v);
30681 this.inputEl.dom.value = this.getValue();
30686 getDay : function()
30688 return this.dayField.getValue();
30691 getMonth : function()
30693 return this.monthField.getValue();
30696 getYear : function()
30698 return this.yearField.getValue();
30701 getValue : function()
30703 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30705 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30715 this.inputEl.dom.value = '';
30720 validate : function()
30722 var d = this.dayField.validate();
30723 var m = this.monthField.validate();
30724 var y = this.yearField.validate();
30729 (!this.dayAllowBlank && !d) ||
30730 (!this.monthAllowBlank && !m) ||
30731 (!this.yearAllowBlank && !y)
30736 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30745 this.markInvalid();
30750 markValid : function()
30753 var label = this.el.select('label', true).first();
30754 var icon = this.el.select('i.fa-star', true).first();
30760 this.fireEvent('valid', this);
30764 * Mark this field as invalid
30765 * @param {String} msg The validation message
30767 markInvalid : function(msg)
30770 var label = this.el.select('label', true).first();
30771 var icon = this.el.select('i.fa-star', true).first();
30773 if(label && !icon){
30774 this.el.select('.roo-date-split-field-label', true).createChild({
30776 cls : 'text-danger fa fa-lg fa-star',
30777 tooltip : 'This field is required',
30778 style : 'margin-right:5px;'
30782 this.fireEvent('invalid', this, msg);
30785 clearInvalid : function()
30787 var label = this.el.select('label', true).first();
30788 var icon = this.el.select('i.fa-star', true).first();
30794 this.fireEvent('valid', this);
30797 getName: function()
30807 * http://masonry.desandro.com
30809 * The idea is to render all the bricks based on vertical width...
30811 * The original code extends 'outlayer' - we might need to use that....
30817 * @class Roo.bootstrap.LayoutMasonry
30818 * @extends Roo.bootstrap.Component
30819 * Bootstrap Layout Masonry class
30822 * Create a new Element
30823 * @param {Object} config The config object
30826 Roo.bootstrap.LayoutMasonry = function(config){
30828 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30832 Roo.bootstrap.LayoutMasonry.register(this);
30838 * Fire after layout the items
30839 * @param {Roo.bootstrap.LayoutMasonry} this
30840 * @param {Roo.EventObject} e
30847 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
30850 * @cfg {Boolean} isLayoutInstant = no animation?
30852 isLayoutInstant : false, // needed?
30855 * @cfg {Number} boxWidth width of the columns
30860 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
30865 * @cfg {Number} padWidth padding below box..
30870 * @cfg {Number} gutter gutter width..
30875 * @cfg {Number} maxCols maximum number of columns
30881 * @cfg {Boolean} isAutoInitial defalut true
30883 isAutoInitial : true,
30888 * @cfg {Boolean} isHorizontal defalut false
30890 isHorizontal : false,
30892 currentSize : null,
30898 bricks: null, //CompositeElement
30902 _isLayoutInited : false,
30904 // isAlternative : false, // only use for vertical layout...
30907 * @cfg {Number} alternativePadWidth padding below box..
30909 alternativePadWidth : 50,
30911 selectedBrick : [],
30913 getAutoCreate : function(){
30915 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30919 cls: 'blog-masonary-wrapper ' + this.cls,
30921 cls : 'mas-boxes masonary'
30928 getChildContainer: function( )
30930 if (this.boxesEl) {
30931 return this.boxesEl;
30934 this.boxesEl = this.el.select('.mas-boxes').first();
30936 return this.boxesEl;
30940 initEvents : function()
30944 if(this.isAutoInitial){
30945 Roo.log('hook children rendered');
30946 this.on('childrenrendered', function() {
30947 Roo.log('children rendered');
30953 initial : function()
30955 this.selectedBrick = [];
30957 this.currentSize = this.el.getBox(true);
30959 Roo.EventManager.onWindowResize(this.resize, this);
30961 if(!this.isAutoInitial){
30969 //this.layout.defer(500,this);
30973 resize : function()
30975 var cs = this.el.getBox(true);
30978 this.currentSize.width == cs.width &&
30979 this.currentSize.x == cs.x &&
30980 this.currentSize.height == cs.height &&
30981 this.currentSize.y == cs.y
30983 Roo.log("no change in with or X or Y");
30987 this.currentSize = cs;
30993 layout : function()
30995 this._resetLayout();
30997 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30999 this.layoutItems( isInstant );
31001 this._isLayoutInited = true;
31003 this.fireEvent('layout', this);
31007 _resetLayout : function()
31009 if(this.isHorizontal){
31010 this.horizontalMeasureColumns();
31014 this.verticalMeasureColumns();
31018 verticalMeasureColumns : function()
31020 this.getContainerWidth();
31022 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31023 // this.colWidth = Math.floor(this.containerWidth * 0.8);
31027 var boxWidth = this.boxWidth + this.padWidth;
31029 if(this.containerWidth < this.boxWidth){
31030 boxWidth = this.containerWidth
31033 var containerWidth = this.containerWidth;
31035 var cols = Math.floor(containerWidth / boxWidth);
31037 this.cols = Math.max( cols, 1 );
31039 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31041 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31043 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31045 this.colWidth = boxWidth + avail - this.padWidth;
31047 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31048 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
31051 horizontalMeasureColumns : function()
31053 this.getContainerWidth();
31055 var boxWidth = this.boxWidth;
31057 if(this.containerWidth < boxWidth){
31058 boxWidth = this.containerWidth;
31061 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31063 this.el.setHeight(boxWidth);
31067 getContainerWidth : function()
31069 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
31072 layoutItems : function( isInstant )
31074 Roo.log(this.bricks);
31076 var items = Roo.apply([], this.bricks);
31078 if(this.isHorizontal){
31079 this._horizontalLayoutItems( items , isInstant );
31083 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31084 // this._verticalAlternativeLayoutItems( items , isInstant );
31088 this._verticalLayoutItems( items , isInstant );
31092 _verticalLayoutItems : function ( items , isInstant)
31094 if ( !items || !items.length ) {
31099 ['xs', 'xs', 'xs', 'tall'],
31100 ['xs', 'xs', 'tall'],
31101 ['xs', 'xs', 'sm'],
31102 ['xs', 'xs', 'xs'],
31108 ['sm', 'xs', 'xs'],
31112 ['tall', 'xs', 'xs', 'xs'],
31113 ['tall', 'xs', 'xs'],
31125 Roo.each(items, function(item, k){
31127 switch (item.size) {
31128 // these layouts take up a full box,
31139 boxes.push([item]);
31162 var filterPattern = function(box, length)
31170 var pattern = box.slice(0, length);
31174 Roo.each(pattern, function(i){
31175 format.push(i.size);
31178 Roo.each(standard, function(s){
31180 if(String(s) != String(format)){
31189 if(!match && length == 1){
31194 filterPattern(box, length - 1);
31198 queue.push(pattern);
31200 box = box.slice(length, box.length);
31202 filterPattern(box, 4);
31208 Roo.each(boxes, function(box, k){
31214 if(box.length == 1){
31219 filterPattern(box, 4);
31223 this._processVerticalLayoutQueue( queue, isInstant );
31227 // _verticalAlternativeLayoutItems : function( items , isInstant )
31229 // if ( !items || !items.length ) {
31233 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
31237 _horizontalLayoutItems : function ( items , isInstant)
31239 if ( !items || !items.length || items.length < 3) {
31245 var eItems = items.slice(0, 3);
31247 items = items.slice(3, items.length);
31250 ['xs', 'xs', 'xs', 'wide'],
31251 ['xs', 'xs', 'wide'],
31252 ['xs', 'xs', 'sm'],
31253 ['xs', 'xs', 'xs'],
31259 ['sm', 'xs', 'xs'],
31263 ['wide', 'xs', 'xs', 'xs'],
31264 ['wide', 'xs', 'xs'],
31277 Roo.each(items, function(item, k){
31279 switch (item.size) {
31290 boxes.push([item]);
31314 var filterPattern = function(box, length)
31322 var pattern = box.slice(0, length);
31326 Roo.each(pattern, function(i){
31327 format.push(i.size);
31330 Roo.each(standard, function(s){
31332 if(String(s) != String(format)){
31341 if(!match && length == 1){
31346 filterPattern(box, length - 1);
31350 queue.push(pattern);
31352 box = box.slice(length, box.length);
31354 filterPattern(box, 4);
31360 Roo.each(boxes, function(box, k){
31366 if(box.length == 1){
31371 filterPattern(box, 4);
31378 var pos = this.el.getBox(true);
31382 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31384 var hit_end = false;
31386 Roo.each(queue, function(box){
31390 Roo.each(box, function(b){
31392 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31402 Roo.each(box, function(b){
31404 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31407 mx = Math.max(mx, b.x);
31411 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31415 Roo.each(box, function(b){
31417 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31431 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31434 /** Sets position of item in DOM
31435 * @param {Element} item
31436 * @param {Number} x - horizontal position
31437 * @param {Number} y - vertical position
31438 * @param {Boolean} isInstant - disables transitions
31440 _processVerticalLayoutQueue : function( queue, isInstant )
31442 var pos = this.el.getBox(true);
31447 for (var i = 0; i < this.cols; i++){
31451 Roo.each(queue, function(box, k){
31453 var col = k % this.cols;
31455 Roo.each(box, function(b,kk){
31457 b.el.position('absolute');
31459 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31460 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31462 if(b.size == 'md-left' || b.size == 'md-right'){
31463 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31464 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31467 b.el.setWidth(width);
31468 b.el.setHeight(height);
31470 b.el.select('iframe',true).setSize(width,height);
31474 for (var i = 0; i < this.cols; i++){
31476 if(maxY[i] < maxY[col]){
31481 col = Math.min(col, i);
31485 x = pos.x + col * (this.colWidth + this.padWidth);
31489 var positions = [];
31491 switch (box.length){
31493 positions = this.getVerticalOneBoxColPositions(x, y, box);
31496 positions = this.getVerticalTwoBoxColPositions(x, y, box);
31499 positions = this.getVerticalThreeBoxColPositions(x, y, box);
31502 positions = this.getVerticalFourBoxColPositions(x, y, box);
31508 Roo.each(box, function(b,kk){
31510 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31512 var sz = b.el.getSize();
31514 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31522 for (var i = 0; i < this.cols; i++){
31523 mY = Math.max(mY, maxY[i]);
31526 this.el.setHeight(mY - pos.y);
31530 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31532 // var pos = this.el.getBox(true);
31535 // var maxX = pos.right;
31537 // var maxHeight = 0;
31539 // Roo.each(items, function(item, k){
31543 // item.el.position('absolute');
31545 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31547 // item.el.setWidth(width);
31549 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31551 // item.el.setHeight(height);
31554 // item.el.setXY([x, y], isInstant ? false : true);
31556 // item.el.setXY([maxX - width, y], isInstant ? false : true);
31559 // y = y + height + this.alternativePadWidth;
31561 // maxHeight = maxHeight + height + this.alternativePadWidth;
31565 // this.el.setHeight(maxHeight);
31569 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31571 var pos = this.el.getBox(true);
31576 var maxX = pos.right;
31578 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31580 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31582 Roo.each(queue, function(box, k){
31584 Roo.each(box, function(b, kk){
31586 b.el.position('absolute');
31588 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31589 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31591 if(b.size == 'md-left' || b.size == 'md-right'){
31592 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31593 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31596 b.el.setWidth(width);
31597 b.el.setHeight(height);
31605 var positions = [];
31607 switch (box.length){
31609 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31612 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31615 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31618 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31624 Roo.each(box, function(b,kk){
31626 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31628 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31636 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31638 Roo.each(eItems, function(b,k){
31640 b.size = (k == 0) ? 'sm' : 'xs';
31641 b.x = (k == 0) ? 2 : 1;
31642 b.y = (k == 0) ? 2 : 1;
31644 b.el.position('absolute');
31646 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31648 b.el.setWidth(width);
31650 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31652 b.el.setHeight(height);
31656 var positions = [];
31659 x : maxX - this.unitWidth * 2 - this.gutter,
31664 x : maxX - this.unitWidth,
31665 y : minY + (this.unitWidth + this.gutter) * 2
31669 x : maxX - this.unitWidth * 3 - this.gutter * 2,
31673 Roo.each(eItems, function(b,k){
31675 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31681 getVerticalOneBoxColPositions : function(x, y, box)
31685 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31687 if(box[0].size == 'md-left'){
31691 if(box[0].size == 'md-right'){
31696 x : x + (this.unitWidth + this.gutter) * rand,
31703 getVerticalTwoBoxColPositions : function(x, y, box)
31707 if(box[0].size == 'xs'){
31711 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31715 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31729 x : x + (this.unitWidth + this.gutter) * 2,
31730 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31737 getVerticalThreeBoxColPositions : function(x, y, box)
31741 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31749 x : x + (this.unitWidth + this.gutter) * 1,
31754 x : x + (this.unitWidth + this.gutter) * 2,
31762 if(box[0].size == 'xs' && box[1].size == 'xs'){
31771 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31775 x : x + (this.unitWidth + this.gutter) * 1,
31789 x : x + (this.unitWidth + this.gutter) * 2,
31794 x : x + (this.unitWidth + this.gutter) * 2,
31795 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31802 getVerticalFourBoxColPositions : function(x, y, box)
31806 if(box[0].size == 'xs'){
31815 y : y + (this.unitHeight + this.gutter) * 1
31820 y : y + (this.unitHeight + this.gutter) * 2
31824 x : x + (this.unitWidth + this.gutter) * 1,
31838 x : x + (this.unitWidth + this.gutter) * 2,
31843 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31844 y : y + (this.unitHeight + this.gutter) * 1
31848 x : x + (this.unitWidth + this.gutter) * 2,
31849 y : y + (this.unitWidth + this.gutter) * 2
31856 getHorizontalOneBoxColPositions : function(maxX, minY, box)
31860 if(box[0].size == 'md-left'){
31862 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31869 if(box[0].size == 'md-right'){
31871 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31872 y : minY + (this.unitWidth + this.gutter) * 1
31878 var rand = Math.floor(Math.random() * (4 - box[0].y));
31881 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31882 y : minY + (this.unitWidth + this.gutter) * rand
31889 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31893 if(box[0].size == 'xs'){
31896 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31901 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31902 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31910 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31915 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31916 y : minY + (this.unitWidth + this.gutter) * 2
31923 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31927 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31930 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31935 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31936 y : minY + (this.unitWidth + this.gutter) * 1
31940 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31941 y : minY + (this.unitWidth + this.gutter) * 2
31948 if(box[0].size == 'xs' && box[1].size == 'xs'){
31951 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31956 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31961 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31962 y : minY + (this.unitWidth + this.gutter) * 1
31970 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31975 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31976 y : minY + (this.unitWidth + this.gutter) * 2
31980 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31981 y : minY + (this.unitWidth + this.gutter) * 2
31988 getHorizontalFourBoxColPositions : function(maxX, minY, box)
31992 if(box[0].size == 'xs'){
31995 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32000 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32005 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),
32010 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32011 y : minY + (this.unitWidth + this.gutter) * 1
32019 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32024 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32025 y : minY + (this.unitWidth + this.gutter) * 2
32029 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32030 y : minY + (this.unitWidth + this.gutter) * 2
32034 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),
32035 y : minY + (this.unitWidth + this.gutter) * 2
32043 * remove a Masonry Brick
32044 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32046 removeBrick : function(brick_id)
32052 for (var i = 0; i<this.bricks.length; i++) {
32053 if (this.bricks[i].id == brick_id) {
32054 this.bricks.splice(i,1);
32055 this.el.dom.removeChild(Roo.get(brick_id).dom);
32062 * adds a Masonry Brick
32063 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32065 addBrick : function(cfg)
32067 var cn = new Roo.bootstrap.MasonryBrick(cfg);
32068 //this.register(cn);
32069 cn.parentId = this.id;
32070 cn.render(this.el);
32075 * register a Masonry Brick
32076 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32079 register : function(brick)
32081 this.bricks.push(brick);
32082 brick.masonryId = this.id;
32086 * clear all the Masonry Brick
32088 clearAll : function()
32091 //this.getChildContainer().dom.innerHTML = "";
32092 this.el.dom.innerHTML = '';
32095 getSelected : function()
32097 if (!this.selectedBrick) {
32101 return this.selectedBrick;
32105 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32109 * register a Masonry Layout
32110 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32113 register : function(layout)
32115 this.groups[layout.id] = layout;
32118 * fetch a Masonry Layout based on the masonry layout ID
32119 * @param {string} the masonry layout to add
32120 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32123 get: function(layout_id) {
32124 if (typeof(this.groups[layout_id]) == 'undefined') {
32127 return this.groups[layout_id] ;
32139 * http://masonry.desandro.com
32141 * The idea is to render all the bricks based on vertical width...
32143 * The original code extends 'outlayer' - we might need to use that....
32149 * @class Roo.bootstrap.LayoutMasonryAuto
32150 * @extends Roo.bootstrap.Component
32151 * Bootstrap Layout Masonry class
32154 * Create a new Element
32155 * @param {Object} config The config object
32158 Roo.bootstrap.LayoutMasonryAuto = function(config){
32159 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32162 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
32165 * @cfg {Boolean} isFitWidth - resize the width..
32167 isFitWidth : false, // options..
32169 * @cfg {Boolean} isOriginLeft = left align?
32171 isOriginLeft : true,
32173 * @cfg {Boolean} isOriginTop = top align?
32175 isOriginTop : false,
32177 * @cfg {Boolean} isLayoutInstant = no animation?
32179 isLayoutInstant : false, // needed?
32181 * @cfg {Boolean} isResizingContainer = not sure if this is used..
32183 isResizingContainer : true,
32185 * @cfg {Number} columnWidth width of the columns
32191 * @cfg {Number} maxCols maximum number of columns
32196 * @cfg {Number} padHeight padding below box..
32202 * @cfg {Boolean} isAutoInitial defalut true
32205 isAutoInitial : true,
32211 initialColumnWidth : 0,
32212 currentSize : null,
32214 colYs : null, // array.
32221 bricks: null, //CompositeElement
32222 cols : 0, // array?
32223 // element : null, // wrapped now this.el
32224 _isLayoutInited : null,
32227 getAutoCreate : function(){
32231 cls: 'blog-masonary-wrapper ' + this.cls,
32233 cls : 'mas-boxes masonary'
32240 getChildContainer: function( )
32242 if (this.boxesEl) {
32243 return this.boxesEl;
32246 this.boxesEl = this.el.select('.mas-boxes').first();
32248 return this.boxesEl;
32252 initEvents : function()
32256 if(this.isAutoInitial){
32257 Roo.log('hook children rendered');
32258 this.on('childrenrendered', function() {
32259 Roo.log('children rendered');
32266 initial : function()
32268 this.reloadItems();
32270 this.currentSize = this.el.getBox(true);
32272 /// was window resize... - let's see if this works..
32273 Roo.EventManager.onWindowResize(this.resize, this);
32275 if(!this.isAutoInitial){
32280 this.layout.defer(500,this);
32283 reloadItems: function()
32285 this.bricks = this.el.select('.masonry-brick', true);
32287 this.bricks.each(function(b) {
32288 //Roo.log(b.getSize());
32289 if (!b.attr('originalwidth')) {
32290 b.attr('originalwidth', b.getSize().width);
32295 Roo.log(this.bricks.elements.length);
32298 resize : function()
32301 var cs = this.el.getBox(true);
32303 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32304 Roo.log("no change in with or X");
32307 this.currentSize = cs;
32311 layout : function()
32314 this._resetLayout();
32315 //this._manageStamps();
32317 // don't animate first layout
32318 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32319 this.layoutItems( isInstant );
32321 // flag for initalized
32322 this._isLayoutInited = true;
32325 layoutItems : function( isInstant )
32327 //var items = this._getItemsForLayout( this.items );
32328 // original code supports filtering layout items.. we just ignore it..
32330 this._layoutItems( this.bricks , isInstant );
32332 this._postLayout();
32334 _layoutItems : function ( items , isInstant)
32336 //this.fireEvent( 'layout', this, items );
32339 if ( !items || !items.elements.length ) {
32340 // no items, emit event with empty array
32345 items.each(function(item) {
32346 Roo.log("layout item");
32348 // get x/y object from method
32349 var position = this._getItemLayoutPosition( item );
32351 position.item = item;
32352 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32353 queue.push( position );
32356 this._processLayoutQueue( queue );
32358 /** Sets position of item in DOM
32359 * @param {Element} item
32360 * @param {Number} x - horizontal position
32361 * @param {Number} y - vertical position
32362 * @param {Boolean} isInstant - disables transitions
32364 _processLayoutQueue : function( queue )
32366 for ( var i=0, len = queue.length; i < len; i++ ) {
32367 var obj = queue[i];
32368 obj.item.position('absolute');
32369 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32375 * Any logic you want to do after each layout,
32376 * i.e. size the container
32378 _postLayout : function()
32380 this.resizeContainer();
32383 resizeContainer : function()
32385 if ( !this.isResizingContainer ) {
32388 var size = this._getContainerSize();
32390 this.el.setSize(size.width,size.height);
32391 this.boxesEl.setSize(size.width,size.height);
32397 _resetLayout : function()
32399 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32400 this.colWidth = this.el.getWidth();
32401 //this.gutter = this.el.getWidth();
32403 this.measureColumns();
32409 this.colYs.push( 0 );
32415 measureColumns : function()
32417 this.getContainerWidth();
32418 // if columnWidth is 0, default to outerWidth of first item
32419 if ( !this.columnWidth ) {
32420 var firstItem = this.bricks.first();
32421 Roo.log(firstItem);
32422 this.columnWidth = this.containerWidth;
32423 if (firstItem && firstItem.attr('originalwidth') ) {
32424 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32426 // columnWidth fall back to item of first element
32427 Roo.log("set column width?");
32428 this.initialColumnWidth = this.columnWidth ;
32430 // if first elem has no width, default to size of container
32435 if (this.initialColumnWidth) {
32436 this.columnWidth = this.initialColumnWidth;
32441 // column width is fixed at the top - however if container width get's smaller we should
32444 // this bit calcs how man columns..
32446 var columnWidth = this.columnWidth += this.gutter;
32448 // calculate columns
32449 var containerWidth = this.containerWidth + this.gutter;
32451 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32452 // fix rounding errors, typically with gutters
32453 var excess = columnWidth - containerWidth % columnWidth;
32456 // if overshoot is less than a pixel, round up, otherwise floor it
32457 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32458 cols = Math[ mathMethod ]( cols );
32459 this.cols = Math.max( cols, 1 );
32460 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32462 // padding positioning..
32463 var totalColWidth = this.cols * this.columnWidth;
32464 var padavail = this.containerWidth - totalColWidth;
32465 // so for 2 columns - we need 3 'pads'
32467 var padNeeded = (1+this.cols) * this.padWidth;
32469 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32471 this.columnWidth += padExtra
32472 //this.padWidth = Math.floor(padavail / ( this.cols));
32474 // adjust colum width so that padding is fixed??
32476 // we have 3 columns ... total = width * 3
32477 // we have X left over... that should be used by
32479 //if (this.expandC) {
32487 getContainerWidth : function()
32489 /* // container is parent if fit width
32490 var container = this.isFitWidth ? this.element.parentNode : this.element;
32491 // check that this.size and size are there
32492 // IE8 triggers resize on body size change, so they might not be
32494 var size = getSize( container ); //FIXME
32495 this.containerWidth = size && size.innerWidth; //FIXME
32498 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
32502 _getItemLayoutPosition : function( item ) // what is item?
32504 // we resize the item to our columnWidth..
32506 item.setWidth(this.columnWidth);
32507 item.autoBoxAdjust = false;
32509 var sz = item.getSize();
32511 // how many columns does this brick span
32512 var remainder = this.containerWidth % this.columnWidth;
32514 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32515 // round if off by 1 pixel, otherwise use ceil
32516 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
32517 colSpan = Math.min( colSpan, this.cols );
32519 // normally this should be '1' as we dont' currently allow multi width columns..
32521 var colGroup = this._getColGroup( colSpan );
32522 // get the minimum Y value from the columns
32523 var minimumY = Math.min.apply( Math, colGroup );
32524 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32526 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
32528 // position the brick
32530 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32531 y: this.currentSize.y + minimumY + this.padHeight
32535 // apply setHeight to necessary columns
32536 var setHeight = minimumY + sz.height + this.padHeight;
32537 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32539 var setSpan = this.cols + 1 - colGroup.length;
32540 for ( var i = 0; i < setSpan; i++ ) {
32541 this.colYs[ shortColIndex + i ] = setHeight ;
32548 * @param {Number} colSpan - number of columns the element spans
32549 * @returns {Array} colGroup
32551 _getColGroup : function( colSpan )
32553 if ( colSpan < 2 ) {
32554 // if brick spans only one column, use all the column Ys
32559 // how many different places could this brick fit horizontally
32560 var groupCount = this.cols + 1 - colSpan;
32561 // for each group potential horizontal position
32562 for ( var i = 0; i < groupCount; i++ ) {
32563 // make an array of colY values for that one group
32564 var groupColYs = this.colYs.slice( i, i + colSpan );
32565 // and get the max value of the array
32566 colGroup[i] = Math.max.apply( Math, groupColYs );
32571 _manageStamp : function( stamp )
32573 var stampSize = stamp.getSize();
32574 var offset = stamp.getBox();
32575 // get the columns that this stamp affects
32576 var firstX = this.isOriginLeft ? offset.x : offset.right;
32577 var lastX = firstX + stampSize.width;
32578 var firstCol = Math.floor( firstX / this.columnWidth );
32579 firstCol = Math.max( 0, firstCol );
32581 var lastCol = Math.floor( lastX / this.columnWidth );
32582 // lastCol should not go over if multiple of columnWidth #425
32583 lastCol -= lastX % this.columnWidth ? 0 : 1;
32584 lastCol = Math.min( this.cols - 1, lastCol );
32586 // set colYs to bottom of the stamp
32587 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32590 for ( var i = firstCol; i <= lastCol; i++ ) {
32591 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32596 _getContainerSize : function()
32598 this.maxY = Math.max.apply( Math, this.colYs );
32603 if ( this.isFitWidth ) {
32604 size.width = this._getContainerFitWidth();
32610 _getContainerFitWidth : function()
32612 var unusedCols = 0;
32613 // count unused columns
32616 if ( this.colYs[i] !== 0 ) {
32621 // fit container to columns that have been used
32622 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32625 needsResizeLayout : function()
32627 var previousWidth = this.containerWidth;
32628 this.getContainerWidth();
32629 return previousWidth !== this.containerWidth;
32644 * @class Roo.bootstrap.MasonryBrick
32645 * @extends Roo.bootstrap.Component
32646 * Bootstrap MasonryBrick class
32649 * Create a new MasonryBrick
32650 * @param {Object} config The config object
32653 Roo.bootstrap.MasonryBrick = function(config){
32655 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32657 Roo.bootstrap.MasonryBrick.register(this);
32663 * When a MasonryBrick is clcik
32664 * @param {Roo.bootstrap.MasonryBrick} this
32665 * @param {Roo.EventObject} e
32671 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
32674 * @cfg {String} title
32678 * @cfg {String} html
32682 * @cfg {String} bgimage
32686 * @cfg {String} videourl
32690 * @cfg {String} cls
32694 * @cfg {String} href
32698 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32703 * @cfg {String} placetitle (center|bottom)
32708 * @cfg {Boolean} isFitContainer defalut true
32710 isFitContainer : true,
32713 * @cfg {Boolean} preventDefault defalut false
32715 preventDefault : false,
32718 * @cfg {Boolean} inverse defalut false
32720 maskInverse : false,
32722 getAutoCreate : function()
32724 if(!this.isFitContainer){
32725 return this.getSplitAutoCreate();
32728 var cls = 'masonry-brick masonry-brick-full';
32730 if(this.href.length){
32731 cls += ' masonry-brick-link';
32734 if(this.bgimage.length){
32735 cls += ' masonry-brick-image';
32738 if(this.maskInverse){
32739 cls += ' mask-inverse';
32742 if(!this.html.length && !this.maskInverse && !this.videourl.length){
32743 cls += ' enable-mask';
32747 cls += ' masonry-' + this.size + '-brick';
32750 if(this.placetitle.length){
32752 switch (this.placetitle) {
32754 cls += ' masonry-center-title';
32757 cls += ' masonry-bottom-title';
32764 if(!this.html.length && !this.bgimage.length){
32765 cls += ' masonry-center-title';
32768 if(!this.html.length && this.bgimage.length){
32769 cls += ' masonry-bottom-title';
32774 cls += ' ' + this.cls;
32778 tag: (this.href.length) ? 'a' : 'div',
32783 cls: 'masonry-brick-mask'
32787 cls: 'masonry-brick-paragraph',
32793 if(this.href.length){
32794 cfg.href = this.href;
32797 var cn = cfg.cn[1].cn;
32799 if(this.title.length){
32802 cls: 'masonry-brick-title',
32807 if(this.html.length){
32810 cls: 'masonry-brick-text',
32815 if (!this.title.length && !this.html.length) {
32816 cfg.cn[1].cls += ' hide';
32819 if(this.bgimage.length){
32822 cls: 'masonry-brick-image-view',
32827 if(this.videourl.length){
32828 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32829 // youtube support only?
32832 cls: 'masonry-brick-image-view',
32835 allowfullscreen : true
32843 getSplitAutoCreate : function()
32845 var cls = 'masonry-brick masonry-brick-split';
32847 if(this.href.length){
32848 cls += ' masonry-brick-link';
32851 if(this.bgimage.length){
32852 cls += ' masonry-brick-image';
32856 cls += ' masonry-' + this.size + '-brick';
32859 switch (this.placetitle) {
32861 cls += ' masonry-center-title';
32864 cls += ' masonry-bottom-title';
32867 if(!this.bgimage.length){
32868 cls += ' masonry-center-title';
32871 if(this.bgimage.length){
32872 cls += ' masonry-bottom-title';
32878 cls += ' ' + this.cls;
32882 tag: (this.href.length) ? 'a' : 'div',
32887 cls: 'masonry-brick-split-head',
32891 cls: 'masonry-brick-paragraph',
32898 cls: 'masonry-brick-split-body',
32904 if(this.href.length){
32905 cfg.href = this.href;
32908 if(this.title.length){
32909 cfg.cn[0].cn[0].cn.push({
32911 cls: 'masonry-brick-title',
32916 if(this.html.length){
32917 cfg.cn[1].cn.push({
32919 cls: 'masonry-brick-text',
32924 if(this.bgimage.length){
32925 cfg.cn[0].cn.push({
32927 cls: 'masonry-brick-image-view',
32932 if(this.videourl.length){
32933 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32934 // youtube support only?
32935 cfg.cn[0].cn.cn.push({
32937 cls: 'masonry-brick-image-view',
32940 allowfullscreen : true
32947 initEvents: function()
32949 switch (this.size) {
32982 this.el.on('touchstart', this.onTouchStart, this);
32983 this.el.on('touchmove', this.onTouchMove, this);
32984 this.el.on('touchend', this.onTouchEnd, this);
32985 this.el.on('contextmenu', this.onContextMenu, this);
32987 this.el.on('mouseenter' ,this.enter, this);
32988 this.el.on('mouseleave', this.leave, this);
32989 this.el.on('click', this.onClick, this);
32992 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32993 this.parent().bricks.push(this);
32998 onClick: function(e, el)
33000 var time = this.endTimer - this.startTimer;
33001 // Roo.log(e.preventDefault());
33004 e.preventDefault();
33009 if(!this.preventDefault){
33013 e.preventDefault();
33015 if (this.activeClass != '') {
33016 this.selectBrick();
33019 this.fireEvent('click', this, e);
33022 enter: function(e, el)
33024 e.preventDefault();
33026 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33030 if(this.bgimage.length && this.html.length){
33031 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33035 leave: function(e, el)
33037 e.preventDefault();
33039 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33043 if(this.bgimage.length && this.html.length){
33044 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33048 onTouchStart: function(e, el)
33050 // e.preventDefault();
33052 this.touchmoved = false;
33054 if(!this.isFitContainer){
33058 if(!this.bgimage.length || !this.html.length){
33062 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33064 this.timer = new Date().getTime();
33068 onTouchMove: function(e, el)
33070 this.touchmoved = true;
33073 onContextMenu : function(e,el)
33075 e.preventDefault();
33076 e.stopPropagation();
33080 onTouchEnd: function(e, el)
33082 // e.preventDefault();
33084 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33091 if(!this.bgimage.length || !this.html.length){
33093 if(this.href.length){
33094 window.location.href = this.href;
33100 if(!this.isFitContainer){
33104 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33106 window.location.href = this.href;
33109 //selection on single brick only
33110 selectBrick : function() {
33112 if (!this.parentId) {
33116 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33117 var index = m.selectedBrick.indexOf(this.id);
33120 m.selectedBrick.splice(index,1);
33121 this.el.removeClass(this.activeClass);
33125 for(var i = 0; i < m.selectedBrick.length; i++) {
33126 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33127 b.el.removeClass(b.activeClass);
33130 m.selectedBrick = [];
33132 m.selectedBrick.push(this.id);
33133 this.el.addClass(this.activeClass);
33137 isSelected : function(){
33138 return this.el.hasClass(this.activeClass);
33143 Roo.apply(Roo.bootstrap.MasonryBrick, {
33146 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33148 * register a Masonry Brick
33149 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33152 register : function(brick)
33154 //this.groups[brick.id] = brick;
33155 this.groups.add(brick.id, brick);
33158 * fetch a masonry brick based on the masonry brick ID
33159 * @param {string} the masonry brick to add
33160 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33163 get: function(brick_id)
33165 // if (typeof(this.groups[brick_id]) == 'undefined') {
33168 // return this.groups[brick_id] ;
33170 if(this.groups.key(brick_id)) {
33171 return this.groups.key(brick_id);
33189 * @class Roo.bootstrap.Brick
33190 * @extends Roo.bootstrap.Component
33191 * Bootstrap Brick class
33194 * Create a new Brick
33195 * @param {Object} config The config object
33198 Roo.bootstrap.Brick = function(config){
33199 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33205 * When a Brick is click
33206 * @param {Roo.bootstrap.Brick} this
33207 * @param {Roo.EventObject} e
33213 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
33216 * @cfg {String} title
33220 * @cfg {String} html
33224 * @cfg {String} bgimage
33228 * @cfg {String} cls
33232 * @cfg {String} href
33236 * @cfg {String} video
33240 * @cfg {Boolean} square
33244 getAutoCreate : function()
33246 var cls = 'roo-brick';
33248 if(this.href.length){
33249 cls += ' roo-brick-link';
33252 if(this.bgimage.length){
33253 cls += ' roo-brick-image';
33256 if(!this.html.length && !this.bgimage.length){
33257 cls += ' roo-brick-center-title';
33260 if(!this.html.length && this.bgimage.length){
33261 cls += ' roo-brick-bottom-title';
33265 cls += ' ' + this.cls;
33269 tag: (this.href.length) ? 'a' : 'div',
33274 cls: 'roo-brick-paragraph',
33280 if(this.href.length){
33281 cfg.href = this.href;
33284 var cn = cfg.cn[0].cn;
33286 if(this.title.length){
33289 cls: 'roo-brick-title',
33294 if(this.html.length){
33297 cls: 'roo-brick-text',
33304 if(this.bgimage.length){
33307 cls: 'roo-brick-image-view',
33315 initEvents: function()
33317 if(this.title.length || this.html.length){
33318 this.el.on('mouseenter' ,this.enter, this);
33319 this.el.on('mouseleave', this.leave, this);
33322 Roo.EventManager.onWindowResize(this.resize, this);
33324 if(this.bgimage.length){
33325 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33326 this.imageEl.on('load', this.onImageLoad, this);
33333 onImageLoad : function()
33338 resize : function()
33340 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33342 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33344 if(this.bgimage.length){
33345 var image = this.el.select('.roo-brick-image-view', true).first();
33347 image.setWidth(paragraph.getWidth());
33350 image.setHeight(paragraph.getWidth());
33353 this.el.setHeight(image.getHeight());
33354 paragraph.setHeight(image.getHeight());
33360 enter: function(e, el)
33362 e.preventDefault();
33364 if(this.bgimage.length){
33365 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33366 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33370 leave: function(e, el)
33372 e.preventDefault();
33374 if(this.bgimage.length){
33375 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33376 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33391 * @class Roo.bootstrap.NumberField
33392 * @extends Roo.bootstrap.Input
33393 * Bootstrap NumberField class
33399 * Create a new NumberField
33400 * @param {Object} config The config object
33403 Roo.bootstrap.NumberField = function(config){
33404 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33407 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33410 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33412 allowDecimals : true,
33414 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33416 decimalSeparator : ".",
33418 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33420 decimalPrecision : 2,
33422 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33424 allowNegative : true,
33427 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33431 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33433 minValue : Number.NEGATIVE_INFINITY,
33435 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33437 maxValue : Number.MAX_VALUE,
33439 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33441 minText : "The minimum value for this field is {0}",
33443 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33445 maxText : "The maximum value for this field is {0}",
33447 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
33448 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33450 nanText : "{0} is not a valid number",
33452 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33454 thousandsDelimiter : false,
33456 * @cfg {String} valueAlign alignment of value
33458 valueAlign : "left",
33460 getAutoCreate : function()
33462 var hiddenInput = {
33466 cls: 'hidden-number-input'
33470 hiddenInput.name = this.name;
33475 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33477 this.name = hiddenInput.name;
33479 if(cfg.cn.length > 0) {
33480 cfg.cn.push(hiddenInput);
33487 initEvents : function()
33489 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33491 var allowed = "0123456789";
33493 if(this.allowDecimals){
33494 allowed += this.decimalSeparator;
33497 if(this.allowNegative){
33501 if(this.thousandsDelimiter) {
33505 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33507 var keyPress = function(e){
33509 var k = e.getKey();
33511 var c = e.getCharCode();
33514 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33515 allowed.indexOf(String.fromCharCode(c)) === -1
33521 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33525 if(allowed.indexOf(String.fromCharCode(c)) === -1){
33530 this.el.on("keypress", keyPress, this);
33533 validateValue : function(value)
33536 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33540 var num = this.parseValue(value);
33543 this.markInvalid(String.format(this.nanText, value));
33547 if(num < this.minValue){
33548 this.markInvalid(String.format(this.minText, this.minValue));
33552 if(num > this.maxValue){
33553 this.markInvalid(String.format(this.maxText, this.maxValue));
33560 getValue : function()
33562 var v = this.hiddenEl().getValue();
33564 return this.fixPrecision(this.parseValue(v));
33567 parseValue : function(value)
33569 if(this.thousandsDelimiter) {
33571 r = new RegExp(",", "g");
33572 value = value.replace(r, "");
33575 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33576 return isNaN(value) ? '' : value;
33579 fixPrecision : function(value)
33581 if(this.thousandsDelimiter) {
33583 r = new RegExp(",", "g");
33584 value = value.replace(r, "");
33587 var nan = isNaN(value);
33589 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33590 return nan ? '' : value;
33592 return parseFloat(value).toFixed(this.decimalPrecision);
33595 setValue : function(v)
33597 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33603 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33605 this.inputEl().dom.value = (v == '') ? '' :
33606 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33608 if(!this.allowZero && v === '0') {
33609 this.hiddenEl().dom.value = '';
33610 this.inputEl().dom.value = '';
33617 decimalPrecisionFcn : function(v)
33619 return Math.floor(v);
33622 beforeBlur : function()
33624 var v = this.parseValue(this.getRawValue());
33626 if(v || v === 0 || v === ''){
33631 hiddenEl : function()
33633 return this.el.select('input.hidden-number-input',true).first();
33645 * @class Roo.bootstrap.DocumentSlider
33646 * @extends Roo.bootstrap.Component
33647 * Bootstrap DocumentSlider class
33650 * Create a new DocumentViewer
33651 * @param {Object} config The config object
33654 Roo.bootstrap.DocumentSlider = function(config){
33655 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33662 * Fire after initEvent
33663 * @param {Roo.bootstrap.DocumentSlider} this
33668 * Fire after update
33669 * @param {Roo.bootstrap.DocumentSlider} this
33675 * @param {Roo.bootstrap.DocumentSlider} this
33681 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
33687 getAutoCreate : function()
33691 cls : 'roo-document-slider',
33695 cls : 'roo-document-slider-header',
33699 cls : 'roo-document-slider-header-title'
33705 cls : 'roo-document-slider-body',
33709 cls : 'roo-document-slider-prev',
33713 cls : 'fa fa-chevron-left'
33719 cls : 'roo-document-slider-thumb',
33723 cls : 'roo-document-slider-image'
33729 cls : 'roo-document-slider-next',
33733 cls : 'fa fa-chevron-right'
33745 initEvents : function()
33747 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33748 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33750 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33751 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33753 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33754 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33756 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33757 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33759 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33760 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33762 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33763 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33765 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33766 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33768 this.thumbEl.on('click', this.onClick, this);
33770 this.prevIndicator.on('click', this.prev, this);
33772 this.nextIndicator.on('click', this.next, this);
33776 initial : function()
33778 if(this.files.length){
33779 this.indicator = 1;
33783 this.fireEvent('initial', this);
33786 update : function()
33788 this.imageEl.attr('src', this.files[this.indicator - 1]);
33790 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33792 this.prevIndicator.show();
33794 if(this.indicator == 1){
33795 this.prevIndicator.hide();
33798 this.nextIndicator.show();
33800 if(this.indicator == this.files.length){
33801 this.nextIndicator.hide();
33804 this.thumbEl.scrollTo('top');
33806 this.fireEvent('update', this);
33809 onClick : function(e)
33811 e.preventDefault();
33813 this.fireEvent('click', this);
33818 e.preventDefault();
33820 this.indicator = Math.max(1, this.indicator - 1);
33827 e.preventDefault();
33829 this.indicator = Math.min(this.files.length, this.indicator + 1);
33843 * @class Roo.bootstrap.RadioSet
33844 * @extends Roo.bootstrap.Input
33845 * Bootstrap RadioSet class
33846 * @cfg {String} indicatorpos (left|right) default left
33847 * @cfg {Boolean} inline (true|false) inline the element (default true)
33848 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33850 * Create a new RadioSet
33851 * @param {Object} config The config object
33854 Roo.bootstrap.RadioSet = function(config){
33856 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33860 Roo.bootstrap.RadioSet.register(this);
33865 * Fires when the element is checked or unchecked.
33866 * @param {Roo.bootstrap.RadioSet} this This radio
33867 * @param {Roo.bootstrap.Radio} item The checked item
33872 * Fires when the element is click.
33873 * @param {Roo.bootstrap.RadioSet} this This radio set
33874 * @param {Roo.bootstrap.Radio} item The checked item
33875 * @param {Roo.EventObject} e The event object
33882 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
33890 indicatorpos : 'left',
33892 getAutoCreate : function()
33896 cls : 'roo-radio-set-label',
33900 html : this.fieldLabel
33905 if(this.indicatorpos == 'left'){
33908 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33909 tooltip : 'This field is required'
33914 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33915 tooltip : 'This field is required'
33921 cls : 'roo-radio-set-items'
33924 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33926 if (align === 'left' && this.fieldLabel.length) {
33929 cls : "roo-radio-set-right",
33935 if(this.labelWidth > 12){
33936 label.style = "width: " + this.labelWidth + 'px';
33939 if(this.labelWidth < 13 && this.labelmd == 0){
33940 this.labelmd = this.labelWidth;
33943 if(this.labellg > 0){
33944 label.cls += ' col-lg-' + this.labellg;
33945 items.cls += ' col-lg-' + (12 - this.labellg);
33948 if(this.labelmd > 0){
33949 label.cls += ' col-md-' + this.labelmd;
33950 items.cls += ' col-md-' + (12 - this.labelmd);
33953 if(this.labelsm > 0){
33954 label.cls += ' col-sm-' + this.labelsm;
33955 items.cls += ' col-sm-' + (12 - this.labelsm);
33958 if(this.labelxs > 0){
33959 label.cls += ' col-xs-' + this.labelxs;
33960 items.cls += ' col-xs-' + (12 - this.labelxs);
33966 cls : 'roo-radio-set',
33970 cls : 'roo-radio-set-input',
33973 value : this.value ? this.value : ''
33980 if(this.weight.length){
33981 cfg.cls += ' roo-radio-' + this.weight;
33985 cfg.cls += ' roo-radio-set-inline';
33989 ['xs','sm','md','lg'].map(function(size){
33990 if (settings[size]) {
33991 cfg.cls += ' col-' + size + '-' + settings[size];
33999 initEvents : function()
34001 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34002 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34004 if(!this.fieldLabel.length){
34005 this.labelEl.hide();
34008 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34009 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34011 this.indicator = this.indicatorEl();
34013 if(this.indicator){
34014 this.indicator.addClass('invisible');
34017 this.originalValue = this.getValue();
34021 inputEl: function ()
34023 return this.el.select('.roo-radio-set-input', true).first();
34026 getChildContainer : function()
34028 return this.itemsEl;
34031 register : function(item)
34033 this.radioes.push(item);
34037 validate : function()
34039 if(this.getVisibilityEl().hasClass('hidden')){
34045 Roo.each(this.radioes, function(i){
34054 if(this.allowBlank) {
34058 if(this.disabled || valid){
34063 this.markInvalid();
34068 markValid : function()
34070 if(this.labelEl.isVisible(true)){
34071 this.indicatorEl().removeClass('visible');
34072 this.indicatorEl().addClass('invisible');
34075 this.el.removeClass([this.invalidClass, this.validClass]);
34076 this.el.addClass(this.validClass);
34078 this.fireEvent('valid', this);
34081 markInvalid : function(msg)
34083 if(this.allowBlank || this.disabled){
34087 if(this.labelEl.isVisible(true)){
34088 this.indicatorEl().removeClass('invisible');
34089 this.indicatorEl().addClass('visible');
34092 this.el.removeClass([this.invalidClass, this.validClass]);
34093 this.el.addClass(this.invalidClass);
34095 this.fireEvent('invalid', this, msg);
34099 setValue : function(v, suppressEvent)
34101 if(this.value === v){
34108 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34111 Roo.each(this.radioes, function(i){
34113 i.el.removeClass('checked');
34116 Roo.each(this.radioes, function(i){
34118 if(i.value === v || i.value.toString() === v.toString()){
34120 i.el.addClass('checked');
34122 if(suppressEvent !== true){
34123 this.fireEvent('check', this, i);
34134 clearInvalid : function(){
34136 if(!this.el || this.preventMark){
34140 this.el.removeClass([this.invalidClass]);
34142 this.fireEvent('valid', this);
34147 Roo.apply(Roo.bootstrap.RadioSet, {
34151 register : function(set)
34153 this.groups[set.name] = set;
34156 get: function(name)
34158 if (typeof(this.groups[name]) == 'undefined') {
34162 return this.groups[name] ;
34168 * Ext JS Library 1.1.1
34169 * Copyright(c) 2006-2007, Ext JS, LLC.
34171 * Originally Released Under LGPL - original licence link has changed is not relivant.
34174 * <script type="text/javascript">
34179 * @class Roo.bootstrap.SplitBar
34180 * @extends Roo.util.Observable
34181 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34185 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34186 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34187 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34188 split.minSize = 100;
34189 split.maxSize = 600;
34190 split.animate = true;
34191 split.on('moved', splitterMoved);
34194 * Create a new SplitBar
34195 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
34196 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
34197 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34198 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
34199 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34200 position of the SplitBar).
34202 Roo.bootstrap.SplitBar = function(cfg){
34207 // dragElement : elm
34208 // resizingElement: el,
34210 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34211 // placement : Roo.bootstrap.SplitBar.LEFT ,
34212 // existingProxy ???
34215 this.el = Roo.get(cfg.dragElement, true);
34216 this.el.dom.unselectable = "on";
34218 this.resizingEl = Roo.get(cfg.resizingElement, true);
34222 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34223 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34226 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34229 * The minimum size of the resizing element. (Defaults to 0)
34235 * The maximum size of the resizing element. (Defaults to 2000)
34238 this.maxSize = 2000;
34241 * Whether to animate the transition to the new size
34244 this.animate = false;
34247 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34250 this.useShim = false;
34255 if(!cfg.existingProxy){
34257 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34259 this.proxy = Roo.get(cfg.existingProxy).dom;
34262 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34265 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34268 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34271 this.dragSpecs = {};
34274 * @private The adapter to use to positon and resize elements
34276 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34277 this.adapter.init(this);
34279 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34281 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34282 this.el.addClass("roo-splitbar-h");
34285 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34286 this.el.addClass("roo-splitbar-v");
34292 * Fires when the splitter is moved (alias for {@link #event-moved})
34293 * @param {Roo.bootstrap.SplitBar} this
34294 * @param {Number} newSize the new width or height
34299 * Fires when the splitter is moved
34300 * @param {Roo.bootstrap.SplitBar} this
34301 * @param {Number} newSize the new width or height
34305 * @event beforeresize
34306 * Fires before the splitter is dragged
34307 * @param {Roo.bootstrap.SplitBar} this
34309 "beforeresize" : true,
34311 "beforeapply" : true
34314 Roo.util.Observable.call(this);
34317 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34318 onStartProxyDrag : function(x, y){
34319 this.fireEvent("beforeresize", this);
34321 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
34323 o.enableDisplayMode("block");
34324 // all splitbars share the same overlay
34325 Roo.bootstrap.SplitBar.prototype.overlay = o;
34327 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34328 this.overlay.show();
34329 Roo.get(this.proxy).setDisplayed("block");
34330 var size = this.adapter.getElementSize(this);
34331 this.activeMinSize = this.getMinimumSize();;
34332 this.activeMaxSize = this.getMaximumSize();;
34333 var c1 = size - this.activeMinSize;
34334 var c2 = Math.max(this.activeMaxSize - size, 0);
34335 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34336 this.dd.resetConstraints();
34337 this.dd.setXConstraint(
34338 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
34339 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34341 this.dd.setYConstraint(0, 0);
34343 this.dd.resetConstraints();
34344 this.dd.setXConstraint(0, 0);
34345 this.dd.setYConstraint(
34346 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
34347 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34350 this.dragSpecs.startSize = size;
34351 this.dragSpecs.startPoint = [x, y];
34352 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34356 * @private Called after the drag operation by the DDProxy
34358 onEndProxyDrag : function(e){
34359 Roo.get(this.proxy).setDisplayed(false);
34360 var endPoint = Roo.lib.Event.getXY(e);
34362 this.overlay.hide();
34365 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34366 newSize = this.dragSpecs.startSize +
34367 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34368 endPoint[0] - this.dragSpecs.startPoint[0] :
34369 this.dragSpecs.startPoint[0] - endPoint[0]
34372 newSize = this.dragSpecs.startSize +
34373 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34374 endPoint[1] - this.dragSpecs.startPoint[1] :
34375 this.dragSpecs.startPoint[1] - endPoint[1]
34378 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34379 if(newSize != this.dragSpecs.startSize){
34380 if(this.fireEvent('beforeapply', this, newSize) !== false){
34381 this.adapter.setElementSize(this, newSize);
34382 this.fireEvent("moved", this, newSize);
34383 this.fireEvent("resize", this, newSize);
34389 * Get the adapter this SplitBar uses
34390 * @return The adapter object
34392 getAdapter : function(){
34393 return this.adapter;
34397 * Set the adapter this SplitBar uses
34398 * @param {Object} adapter A SplitBar adapter object
34400 setAdapter : function(adapter){
34401 this.adapter = adapter;
34402 this.adapter.init(this);
34406 * Gets the minimum size for the resizing element
34407 * @return {Number} The minimum size
34409 getMinimumSize : function(){
34410 return this.minSize;
34414 * Sets the minimum size for the resizing element
34415 * @param {Number} minSize The minimum size
34417 setMinimumSize : function(minSize){
34418 this.minSize = minSize;
34422 * Gets the maximum size for the resizing element
34423 * @return {Number} The maximum size
34425 getMaximumSize : function(){
34426 return this.maxSize;
34430 * Sets the maximum size for the resizing element
34431 * @param {Number} maxSize The maximum size
34433 setMaximumSize : function(maxSize){
34434 this.maxSize = maxSize;
34438 * Sets the initialize size for the resizing element
34439 * @param {Number} size The initial size
34441 setCurrentSize : function(size){
34442 var oldAnimate = this.animate;
34443 this.animate = false;
34444 this.adapter.setElementSize(this, size);
34445 this.animate = oldAnimate;
34449 * Destroy this splitbar.
34450 * @param {Boolean} removeEl True to remove the element
34452 destroy : function(removeEl){
34454 this.shim.remove();
34457 this.proxy.parentNode.removeChild(this.proxy);
34465 * @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.
34467 Roo.bootstrap.SplitBar.createProxy = function(dir){
34468 var proxy = new Roo.Element(document.createElement("div"));
34469 proxy.unselectable();
34470 var cls = 'roo-splitbar-proxy';
34471 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34472 document.body.appendChild(proxy.dom);
34477 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34478 * Default Adapter. It assumes the splitter and resizing element are not positioned
34479 * elements and only gets/sets the width of the element. Generally used for table based layouts.
34481 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34484 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34485 // do nothing for now
34486 init : function(s){
34490 * Called before drag operations to get the current size of the resizing element.
34491 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34493 getElementSize : function(s){
34494 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34495 return s.resizingEl.getWidth();
34497 return s.resizingEl.getHeight();
34502 * Called after drag operations to set the size of the resizing element.
34503 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34504 * @param {Number} newSize The new size to set
34505 * @param {Function} onComplete A function to be invoked when resizing is complete
34507 setElementSize : function(s, newSize, onComplete){
34508 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34510 s.resizingEl.setWidth(newSize);
34512 onComplete(s, newSize);
34515 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34520 s.resizingEl.setHeight(newSize);
34522 onComplete(s, newSize);
34525 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34532 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34533 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34534 * Adapter that moves the splitter element to align with the resized sizing element.
34535 * Used with an absolute positioned SplitBar.
34536 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34537 * document.body, make sure you assign an id to the body element.
34539 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34540 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34541 this.container = Roo.get(container);
34544 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34545 init : function(s){
34546 this.basic.init(s);
34549 getElementSize : function(s){
34550 return this.basic.getElementSize(s);
34553 setElementSize : function(s, newSize, onComplete){
34554 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34557 moveSplitter : function(s){
34558 var yes = Roo.bootstrap.SplitBar;
34559 switch(s.placement){
34561 s.el.setX(s.resizingEl.getRight());
34564 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34567 s.el.setY(s.resizingEl.getBottom());
34570 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34577 * Orientation constant - Create a vertical SplitBar
34581 Roo.bootstrap.SplitBar.VERTICAL = 1;
34584 * Orientation constant - Create a horizontal SplitBar
34588 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34591 * Placement constant - The resizing element is to the left of the splitter element
34595 Roo.bootstrap.SplitBar.LEFT = 1;
34598 * Placement constant - The resizing element is to the right of the splitter element
34602 Roo.bootstrap.SplitBar.RIGHT = 2;
34605 * Placement constant - The resizing element is positioned above the splitter element
34609 Roo.bootstrap.SplitBar.TOP = 3;
34612 * Placement constant - The resizing element is positioned under splitter element
34616 Roo.bootstrap.SplitBar.BOTTOM = 4;
34617 Roo.namespace("Roo.bootstrap.layout");/*
34619 * Ext JS Library 1.1.1
34620 * Copyright(c) 2006-2007, Ext JS, LLC.
34622 * Originally Released Under LGPL - original licence link has changed is not relivant.
34625 * <script type="text/javascript">
34629 * @class Roo.bootstrap.layout.Manager
34630 * @extends Roo.bootstrap.Component
34631 * Base class for layout managers.
34633 Roo.bootstrap.layout.Manager = function(config)
34635 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34641 /** false to disable window resize monitoring @type Boolean */
34642 this.monitorWindowResize = true;
34647 * Fires when a layout is performed.
34648 * @param {Roo.LayoutManager} this
34652 * @event regionresized
34653 * Fires when the user resizes a region.
34654 * @param {Roo.LayoutRegion} region The resized region
34655 * @param {Number} newSize The new size (width for east/west, height for north/south)
34657 "regionresized" : true,
34659 * @event regioncollapsed
34660 * Fires when a region is collapsed.
34661 * @param {Roo.LayoutRegion} region The collapsed region
34663 "regioncollapsed" : true,
34665 * @event regionexpanded
34666 * Fires when a region is expanded.
34667 * @param {Roo.LayoutRegion} region The expanded region
34669 "regionexpanded" : true
34671 this.updating = false;
34674 this.el = Roo.get(config.el);
34680 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34685 monitorWindowResize : true,
34691 onRender : function(ct, position)
34694 this.el = Roo.get(ct);
34697 //this.fireEvent('render',this);
34701 initEvents: function()
34705 // ie scrollbar fix
34706 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34707 document.body.scroll = "no";
34708 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34709 this.el.position('relative');
34711 this.id = this.el.id;
34712 this.el.addClass("roo-layout-container");
34713 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34714 if(this.el.dom != document.body ) {
34715 this.el.on('resize', this.layout,this);
34716 this.el.on('show', this.layout,this);
34722 * Returns true if this layout is currently being updated
34723 * @return {Boolean}
34725 isUpdating : function(){
34726 return this.updating;
34730 * Suspend the LayoutManager from doing auto-layouts while
34731 * making multiple add or remove calls
34733 beginUpdate : function(){
34734 this.updating = true;
34738 * Restore auto-layouts and optionally disable the manager from performing a layout
34739 * @param {Boolean} noLayout true to disable a layout update
34741 endUpdate : function(noLayout){
34742 this.updating = false;
34748 layout: function(){
34752 onRegionResized : function(region, newSize){
34753 this.fireEvent("regionresized", region, newSize);
34757 onRegionCollapsed : function(region){
34758 this.fireEvent("regioncollapsed", region);
34761 onRegionExpanded : function(region){
34762 this.fireEvent("regionexpanded", region);
34766 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34767 * performs box-model adjustments.
34768 * @return {Object} The size as an object {width: (the width), height: (the height)}
34770 getViewSize : function()
34773 if(this.el.dom != document.body){
34774 size = this.el.getSize();
34776 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34778 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34779 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34784 * Returns the Element this layout is bound to.
34785 * @return {Roo.Element}
34787 getEl : function(){
34792 * Returns the specified region.
34793 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34794 * @return {Roo.LayoutRegion}
34796 getRegion : function(target){
34797 return this.regions[target.toLowerCase()];
34800 onWindowResize : function(){
34801 if(this.monitorWindowResize){
34808 * Ext JS Library 1.1.1
34809 * Copyright(c) 2006-2007, Ext JS, LLC.
34811 * Originally Released Under LGPL - original licence link has changed is not relivant.
34814 * <script type="text/javascript">
34817 * @class Roo.bootstrap.layout.Border
34818 * @extends Roo.bootstrap.layout.Manager
34819 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34820 * please see: examples/bootstrap/nested.html<br><br>
34822 <b>The container the layout is rendered into can be either the body element or any other element.
34823 If it is not the body element, the container needs to either be an absolute positioned element,
34824 or you will need to add "position:relative" to the css of the container. You will also need to specify
34825 the container size if it is not the body element.</b>
34828 * Create a new Border
34829 * @param {Object} config Configuration options
34831 Roo.bootstrap.layout.Border = function(config){
34832 config = config || {};
34833 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34837 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34838 if(config[region]){
34839 config[region].region = region;
34840 this.addRegion(config[region]);
34846 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
34848 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34850 * Creates and adds a new region if it doesn't already exist.
34851 * @param {String} target The target region key (north, south, east, west or center).
34852 * @param {Object} config The regions config object
34853 * @return {BorderLayoutRegion} The new region
34855 addRegion : function(config)
34857 if(!this.regions[config.region]){
34858 var r = this.factory(config);
34859 this.bindRegion(r);
34861 return this.regions[config.region];
34865 bindRegion : function(r){
34866 this.regions[r.config.region] = r;
34868 r.on("visibilitychange", this.layout, this);
34869 r.on("paneladded", this.layout, this);
34870 r.on("panelremoved", this.layout, this);
34871 r.on("invalidated", this.layout, this);
34872 r.on("resized", this.onRegionResized, this);
34873 r.on("collapsed", this.onRegionCollapsed, this);
34874 r.on("expanded", this.onRegionExpanded, this);
34878 * Performs a layout update.
34880 layout : function()
34882 if(this.updating) {
34886 // render all the rebions if they have not been done alreayd?
34887 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34888 if(this.regions[region] && !this.regions[region].bodyEl){
34889 this.regions[region].onRender(this.el)
34893 var size = this.getViewSize();
34894 var w = size.width;
34895 var h = size.height;
34900 //var x = 0, y = 0;
34902 var rs = this.regions;
34903 var north = rs["north"];
34904 var south = rs["south"];
34905 var west = rs["west"];
34906 var east = rs["east"];
34907 var center = rs["center"];
34908 //if(this.hideOnLayout){ // not supported anymore
34909 //c.el.setStyle("display", "none");
34911 if(north && north.isVisible()){
34912 var b = north.getBox();
34913 var m = north.getMargins();
34914 b.width = w - (m.left+m.right);
34917 centerY = b.height + b.y + m.bottom;
34918 centerH -= centerY;
34919 north.updateBox(this.safeBox(b));
34921 if(south && south.isVisible()){
34922 var b = south.getBox();
34923 var m = south.getMargins();
34924 b.width = w - (m.left+m.right);
34926 var totalHeight = (b.height + m.top + m.bottom);
34927 b.y = h - totalHeight + m.top;
34928 centerH -= totalHeight;
34929 south.updateBox(this.safeBox(b));
34931 if(west && west.isVisible()){
34932 var b = west.getBox();
34933 var m = west.getMargins();
34934 b.height = centerH - (m.top+m.bottom);
34936 b.y = centerY + m.top;
34937 var totalWidth = (b.width + m.left + m.right);
34938 centerX += totalWidth;
34939 centerW -= totalWidth;
34940 west.updateBox(this.safeBox(b));
34942 if(east && east.isVisible()){
34943 var b = east.getBox();
34944 var m = east.getMargins();
34945 b.height = centerH - (m.top+m.bottom);
34946 var totalWidth = (b.width + m.left + m.right);
34947 b.x = w - totalWidth + m.left;
34948 b.y = centerY + m.top;
34949 centerW -= totalWidth;
34950 east.updateBox(this.safeBox(b));
34953 var m = center.getMargins();
34955 x: centerX + m.left,
34956 y: centerY + m.top,
34957 width: centerW - (m.left+m.right),
34958 height: centerH - (m.top+m.bottom)
34960 //if(this.hideOnLayout){
34961 //center.el.setStyle("display", "block");
34963 center.updateBox(this.safeBox(centerBox));
34966 this.fireEvent("layout", this);
34970 safeBox : function(box){
34971 box.width = Math.max(0, box.width);
34972 box.height = Math.max(0, box.height);
34977 * Adds a ContentPanel (or subclass) to this layout.
34978 * @param {String} target The target region key (north, south, east, west or center).
34979 * @param {Roo.ContentPanel} panel The panel to add
34980 * @return {Roo.ContentPanel} The added panel
34982 add : function(target, panel){
34984 target = target.toLowerCase();
34985 return this.regions[target].add(panel);
34989 * Remove a ContentPanel (or subclass) to this layout.
34990 * @param {String} target The target region key (north, south, east, west or center).
34991 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34992 * @return {Roo.ContentPanel} The removed panel
34994 remove : function(target, panel){
34995 target = target.toLowerCase();
34996 return this.regions[target].remove(panel);
35000 * Searches all regions for a panel with the specified id
35001 * @param {String} panelId
35002 * @return {Roo.ContentPanel} The panel or null if it wasn't found
35004 findPanel : function(panelId){
35005 var rs = this.regions;
35006 for(var target in rs){
35007 if(typeof rs[target] != "function"){
35008 var p = rs[target].getPanel(panelId);
35018 * Searches all regions for a panel with the specified id and activates (shows) it.
35019 * @param {String/ContentPanel} panelId The panels id or the panel itself
35020 * @return {Roo.ContentPanel} The shown panel or null
35022 showPanel : function(panelId) {
35023 var rs = this.regions;
35024 for(var target in rs){
35025 var r = rs[target];
35026 if(typeof r != "function"){
35027 if(r.hasPanel(panelId)){
35028 return r.showPanel(panelId);
35036 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35037 * @param {Roo.state.Provider} provider (optional) An alternate state provider
35040 restoreState : function(provider){
35042 provider = Roo.state.Manager;
35044 var sm = new Roo.LayoutStateManager();
35045 sm.init(this, provider);
35051 * Adds a xtype elements to the layout.
35055 xtype : 'ContentPanel',
35062 xtype : 'NestedLayoutPanel',
35068 items : [ ... list of content panels or nested layout panels.. ]
35072 * @param {Object} cfg Xtype definition of item to add.
35074 addxtype : function(cfg)
35076 // basically accepts a pannel...
35077 // can accept a layout region..!?!?
35078 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35081 // theory? children can only be panels??
35083 //if (!cfg.xtype.match(/Panel$/)) {
35088 if (typeof(cfg.region) == 'undefined') {
35089 Roo.log("Failed to add Panel, region was not set");
35093 var region = cfg.region;
35099 xitems = cfg.items;
35106 case 'Content': // ContentPanel (el, cfg)
35107 case 'Scroll': // ContentPanel (el, cfg)
35109 cfg.autoCreate = true;
35110 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35112 // var el = this.el.createChild();
35113 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35116 this.add(region, ret);
35120 case 'TreePanel': // our new panel!
35121 cfg.el = this.el.createChild();
35122 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35123 this.add(region, ret);
35128 // create a new Layout (which is a Border Layout...
35130 var clayout = cfg.layout;
35131 clayout.el = this.el.createChild();
35132 clayout.items = clayout.items || [];
35136 // replace this exitems with the clayout ones..
35137 xitems = clayout.items;
35139 // force background off if it's in center...
35140 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35141 cfg.background = false;
35143 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
35146 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35147 //console.log('adding nested layout panel ' + cfg.toSource());
35148 this.add(region, ret);
35149 nb = {}; /// find first...
35154 // needs grid and region
35156 //var el = this.getRegion(region).el.createChild();
35158 *var el = this.el.createChild();
35159 // create the grid first...
35160 cfg.grid.container = el;
35161 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35164 if (region == 'center' && this.active ) {
35165 cfg.background = false;
35168 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35170 this.add(region, ret);
35172 if (cfg.background) {
35173 // render grid on panel activation (if panel background)
35174 ret.on('activate', function(gp) {
35175 if (!gp.grid.rendered) {
35176 // gp.grid.render(el);
35180 // cfg.grid.render(el);
35186 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35187 // it was the old xcomponent building that caused this before.
35188 // espeically if border is the top element in the tree.
35198 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35200 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35201 this.add(region, ret);
35205 throw "Can not add '" + cfg.xtype + "' to Border";
35211 this.beginUpdate();
35215 Roo.each(xitems, function(i) {
35216 region = nb && i.region ? i.region : false;
35218 var add = ret.addxtype(i);
35221 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35222 if (!i.background) {
35223 abn[region] = nb[region] ;
35230 // make the last non-background panel active..
35231 //if (nb) { Roo.log(abn); }
35234 for(var r in abn) {
35235 region = this.getRegion(r);
35237 // tried using nb[r], but it does not work..
35239 region.showPanel(abn[r]);
35250 factory : function(cfg)
35253 var validRegions = Roo.bootstrap.layout.Border.regions;
35255 var target = cfg.region;
35258 var r = Roo.bootstrap.layout;
35262 return new r.North(cfg);
35264 return new r.South(cfg);
35266 return new r.East(cfg);
35268 return new r.West(cfg);
35270 return new r.Center(cfg);
35272 throw 'Layout region "'+target+'" not supported.';
35279 * Ext JS Library 1.1.1
35280 * Copyright(c) 2006-2007, Ext JS, LLC.
35282 * Originally Released Under LGPL - original licence link has changed is not relivant.
35285 * <script type="text/javascript">
35289 * @class Roo.bootstrap.layout.Basic
35290 * @extends Roo.util.Observable
35291 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35292 * and does not have a titlebar, tabs or any other features. All it does is size and position
35293 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35294 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35295 * @cfg {string} region the region that it inhabits..
35296 * @cfg {bool} skipConfig skip config?
35300 Roo.bootstrap.layout.Basic = function(config){
35302 this.mgr = config.mgr;
35304 this.position = config.region;
35306 var skipConfig = config.skipConfig;
35310 * @scope Roo.BasicLayoutRegion
35314 * @event beforeremove
35315 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35316 * @param {Roo.LayoutRegion} this
35317 * @param {Roo.ContentPanel} panel The panel
35318 * @param {Object} e The cancel event object
35320 "beforeremove" : true,
35322 * @event invalidated
35323 * Fires when the layout for this region is changed.
35324 * @param {Roo.LayoutRegion} this
35326 "invalidated" : true,
35328 * @event visibilitychange
35329 * Fires when this region is shown or hidden
35330 * @param {Roo.LayoutRegion} this
35331 * @param {Boolean} visibility true or false
35333 "visibilitychange" : true,
35335 * @event paneladded
35336 * Fires when a panel is added.
35337 * @param {Roo.LayoutRegion} this
35338 * @param {Roo.ContentPanel} panel The panel
35340 "paneladded" : true,
35342 * @event panelremoved
35343 * Fires when a panel is removed.
35344 * @param {Roo.LayoutRegion} this
35345 * @param {Roo.ContentPanel} panel The panel
35347 "panelremoved" : true,
35349 * @event beforecollapse
35350 * Fires when this region before collapse.
35351 * @param {Roo.LayoutRegion} this
35353 "beforecollapse" : true,
35356 * Fires when this region is collapsed.
35357 * @param {Roo.LayoutRegion} this
35359 "collapsed" : true,
35362 * Fires when this region is expanded.
35363 * @param {Roo.LayoutRegion} this
35368 * Fires when this region is slid into view.
35369 * @param {Roo.LayoutRegion} this
35371 "slideshow" : true,
35374 * Fires when this region slides out of view.
35375 * @param {Roo.LayoutRegion} this
35377 "slidehide" : true,
35379 * @event panelactivated
35380 * Fires when a panel is activated.
35381 * @param {Roo.LayoutRegion} this
35382 * @param {Roo.ContentPanel} panel The activated panel
35384 "panelactivated" : true,
35387 * Fires when the user resizes this region.
35388 * @param {Roo.LayoutRegion} this
35389 * @param {Number} newSize The new size (width for east/west, height for north/south)
35393 /** A collection of panels in this region. @type Roo.util.MixedCollection */
35394 this.panels = new Roo.util.MixedCollection();
35395 this.panels.getKey = this.getPanelId.createDelegate(this);
35397 this.activePanel = null;
35398 // ensure listeners are added...
35400 if (config.listeners || config.events) {
35401 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35402 listeners : config.listeners || {},
35403 events : config.events || {}
35407 if(skipConfig !== true){
35408 this.applyConfig(config);
35412 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35414 getPanelId : function(p){
35418 applyConfig : function(config){
35419 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35420 this.config = config;
35425 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
35426 * the width, for horizontal (north, south) the height.
35427 * @param {Number} newSize The new width or height
35429 resizeTo : function(newSize){
35430 var el = this.el ? this.el :
35431 (this.activePanel ? this.activePanel.getEl() : null);
35433 switch(this.position){
35436 el.setWidth(newSize);
35437 this.fireEvent("resized", this, newSize);
35441 el.setHeight(newSize);
35442 this.fireEvent("resized", this, newSize);
35448 getBox : function(){
35449 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35452 getMargins : function(){
35453 return this.margins;
35456 updateBox : function(box){
35458 var el = this.activePanel.getEl();
35459 el.dom.style.left = box.x + "px";
35460 el.dom.style.top = box.y + "px";
35461 this.activePanel.setSize(box.width, box.height);
35465 * Returns the container element for this region.
35466 * @return {Roo.Element}
35468 getEl : function(){
35469 return this.activePanel;
35473 * Returns true if this region is currently visible.
35474 * @return {Boolean}
35476 isVisible : function(){
35477 return this.activePanel ? true : false;
35480 setActivePanel : function(panel){
35481 panel = this.getPanel(panel);
35482 if(this.activePanel && this.activePanel != panel){
35483 this.activePanel.setActiveState(false);
35484 this.activePanel.getEl().setLeftTop(-10000,-10000);
35486 this.activePanel = panel;
35487 panel.setActiveState(true);
35489 panel.setSize(this.box.width, this.box.height);
35491 this.fireEvent("panelactivated", this, panel);
35492 this.fireEvent("invalidated");
35496 * Show the specified panel.
35497 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35498 * @return {Roo.ContentPanel} The shown panel or null
35500 showPanel : function(panel){
35501 panel = this.getPanel(panel);
35503 this.setActivePanel(panel);
35509 * Get the active panel for this region.
35510 * @return {Roo.ContentPanel} The active panel or null
35512 getActivePanel : function(){
35513 return this.activePanel;
35517 * Add the passed ContentPanel(s)
35518 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35519 * @return {Roo.ContentPanel} The panel added (if only one was added)
35521 add : function(panel){
35522 if(arguments.length > 1){
35523 for(var i = 0, len = arguments.length; i < len; i++) {
35524 this.add(arguments[i]);
35528 if(this.hasPanel(panel)){
35529 this.showPanel(panel);
35532 var el = panel.getEl();
35533 if(el.dom.parentNode != this.mgr.el.dom){
35534 this.mgr.el.dom.appendChild(el.dom);
35536 if(panel.setRegion){
35537 panel.setRegion(this);
35539 this.panels.add(panel);
35540 el.setStyle("position", "absolute");
35541 if(!panel.background){
35542 this.setActivePanel(panel);
35543 if(this.config.initialSize && this.panels.getCount()==1){
35544 this.resizeTo(this.config.initialSize);
35547 this.fireEvent("paneladded", this, panel);
35552 * Returns true if the panel is in this region.
35553 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35554 * @return {Boolean}
35556 hasPanel : function(panel){
35557 if(typeof panel == "object"){ // must be panel obj
35558 panel = panel.getId();
35560 return this.getPanel(panel) ? true : false;
35564 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35565 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35566 * @param {Boolean} preservePanel Overrides the config preservePanel option
35567 * @return {Roo.ContentPanel} The panel that was removed
35569 remove : function(panel, preservePanel){
35570 panel = this.getPanel(panel);
35575 this.fireEvent("beforeremove", this, panel, e);
35576 if(e.cancel === true){
35579 var panelId = panel.getId();
35580 this.panels.removeKey(panelId);
35585 * Returns the panel specified or null if it's not in this region.
35586 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35587 * @return {Roo.ContentPanel}
35589 getPanel : function(id){
35590 if(typeof id == "object"){ // must be panel obj
35593 return this.panels.get(id);
35597 * Returns this regions position (north/south/east/west/center).
35600 getPosition: function(){
35601 return this.position;
35605 * Ext JS Library 1.1.1
35606 * Copyright(c) 2006-2007, Ext JS, LLC.
35608 * Originally Released Under LGPL - original licence link has changed is not relivant.
35611 * <script type="text/javascript">
35615 * @class Roo.bootstrap.layout.Region
35616 * @extends Roo.bootstrap.layout.Basic
35617 * This class represents a region in a layout manager.
35619 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35620 * @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})
35621 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
35622 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
35623 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
35624 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
35625 * @cfg {String} title The title for the region (overrides panel titles)
35626 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
35627 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35628 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
35629 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35630 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
35631 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35632 * the space available, similar to FireFox 1.5 tabs (defaults to false)
35633 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
35634 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
35635 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
35637 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
35638 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
35639 * @cfg {Boolean} disableTabTips True to disable tab tooltips
35640 * @cfg {Number} width For East/West panels
35641 * @cfg {Number} height For North/South panels
35642 * @cfg {Boolean} split To show the splitter
35643 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
35645 * @cfg {string} cls Extra CSS classes to add to region
35647 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35648 * @cfg {string} region the region that it inhabits..
35651 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
35652 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
35654 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
35655 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
35656 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
35658 Roo.bootstrap.layout.Region = function(config)
35660 this.applyConfig(config);
35662 var mgr = config.mgr;
35663 var pos = config.region;
35664 config.skipConfig = true;
35665 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35668 this.onRender(mgr.el);
35671 this.visible = true;
35672 this.collapsed = false;
35673 this.unrendered_panels = [];
35676 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35678 position: '', // set by wrapper (eg. north/south etc..)
35679 unrendered_panels : null, // unrendered panels.
35680 createBody : function(){
35681 /** This region's body element
35682 * @type Roo.Element */
35683 this.bodyEl = this.el.createChild({
35685 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35689 onRender: function(ctr, pos)
35691 var dh = Roo.DomHelper;
35692 /** This region's container element
35693 * @type Roo.Element */
35694 this.el = dh.append(ctr.dom, {
35696 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35698 /** This region's title element
35699 * @type Roo.Element */
35701 this.titleEl = dh.append(this.el.dom,
35704 unselectable: "on",
35705 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35707 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
35708 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35711 this.titleEl.enableDisplayMode();
35712 /** This region's title text element
35713 * @type HTMLElement */
35714 this.titleTextEl = this.titleEl.dom.firstChild;
35715 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35717 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35718 this.closeBtn.enableDisplayMode();
35719 this.closeBtn.on("click", this.closeClicked, this);
35720 this.closeBtn.hide();
35722 this.createBody(this.config);
35723 if(this.config.hideWhenEmpty){
35725 this.on("paneladded", this.validateVisibility, this);
35726 this.on("panelremoved", this.validateVisibility, this);
35728 if(this.autoScroll){
35729 this.bodyEl.setStyle("overflow", "auto");
35731 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35733 //if(c.titlebar !== false){
35734 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35735 this.titleEl.hide();
35737 this.titleEl.show();
35738 if(this.config.title){
35739 this.titleTextEl.innerHTML = this.config.title;
35743 if(this.config.collapsed){
35744 this.collapse(true);
35746 if(this.config.hidden){
35750 if (this.unrendered_panels && this.unrendered_panels.length) {
35751 for (var i =0;i< this.unrendered_panels.length; i++) {
35752 this.add(this.unrendered_panels[i]);
35754 this.unrendered_panels = null;
35760 applyConfig : function(c)
35763 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35764 var dh = Roo.DomHelper;
35765 if(c.titlebar !== false){
35766 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35767 this.collapseBtn.on("click", this.collapse, this);
35768 this.collapseBtn.enableDisplayMode();
35770 if(c.showPin === true || this.showPin){
35771 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35772 this.stickBtn.enableDisplayMode();
35773 this.stickBtn.on("click", this.expand, this);
35774 this.stickBtn.hide();
35779 /** This region's collapsed element
35780 * @type Roo.Element */
35783 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35784 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35787 if(c.floatable !== false){
35788 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35789 this.collapsedEl.on("click", this.collapseClick, this);
35792 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35793 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35794 id: "message", unselectable: "on", style:{"float":"left"}});
35795 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35797 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35798 this.expandBtn.on("click", this.expand, this);
35802 if(this.collapseBtn){
35803 this.collapseBtn.setVisible(c.collapsible == true);
35806 this.cmargins = c.cmargins || this.cmargins ||
35807 (this.position == "west" || this.position == "east" ?
35808 {top: 0, left: 2, right:2, bottom: 0} :
35809 {top: 2, left: 0, right:0, bottom: 2});
35811 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35814 this.bottomTabs = c.tabPosition != "top";
35816 this.autoScroll = c.autoScroll || false;
35821 this.duration = c.duration || .30;
35822 this.slideDuration = c.slideDuration || .45;
35827 * Returns true if this region is currently visible.
35828 * @return {Boolean}
35830 isVisible : function(){
35831 return this.visible;
35835 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35836 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
35838 //setCollapsedTitle : function(title){
35839 // title = title || " ";
35840 // if(this.collapsedTitleTextEl){
35841 // this.collapsedTitleTextEl.innerHTML = title;
35845 getBox : function(){
35847 // if(!this.collapsed){
35848 b = this.el.getBox(false, true);
35850 // b = this.collapsedEl.getBox(false, true);
35855 getMargins : function(){
35856 return this.margins;
35857 //return this.collapsed ? this.cmargins : this.margins;
35860 highlight : function(){
35861 this.el.addClass("x-layout-panel-dragover");
35864 unhighlight : function(){
35865 this.el.removeClass("x-layout-panel-dragover");
35868 updateBox : function(box)
35870 if (!this.bodyEl) {
35871 return; // not rendered yet..
35875 if(!this.collapsed){
35876 this.el.dom.style.left = box.x + "px";
35877 this.el.dom.style.top = box.y + "px";
35878 this.updateBody(box.width, box.height);
35880 this.collapsedEl.dom.style.left = box.x + "px";
35881 this.collapsedEl.dom.style.top = box.y + "px";
35882 this.collapsedEl.setSize(box.width, box.height);
35885 this.tabs.autoSizeTabs();
35889 updateBody : function(w, h)
35892 this.el.setWidth(w);
35893 w -= this.el.getBorderWidth("rl");
35894 if(this.config.adjustments){
35895 w += this.config.adjustments[0];
35898 if(h !== null && h > 0){
35899 this.el.setHeight(h);
35900 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35901 h -= this.el.getBorderWidth("tb");
35902 if(this.config.adjustments){
35903 h += this.config.adjustments[1];
35905 this.bodyEl.setHeight(h);
35907 h = this.tabs.syncHeight(h);
35910 if(this.panelSize){
35911 w = w !== null ? w : this.panelSize.width;
35912 h = h !== null ? h : this.panelSize.height;
35914 if(this.activePanel){
35915 var el = this.activePanel.getEl();
35916 w = w !== null ? w : el.getWidth();
35917 h = h !== null ? h : el.getHeight();
35918 this.panelSize = {width: w, height: h};
35919 this.activePanel.setSize(w, h);
35921 if(Roo.isIE && this.tabs){
35922 this.tabs.el.repaint();
35927 * Returns the container element for this region.
35928 * @return {Roo.Element}
35930 getEl : function(){
35935 * Hides this region.
35938 //if(!this.collapsed){
35939 this.el.dom.style.left = "-2000px";
35942 // this.collapsedEl.dom.style.left = "-2000px";
35943 // this.collapsedEl.hide();
35945 this.visible = false;
35946 this.fireEvent("visibilitychange", this, false);
35950 * Shows this region if it was previously hidden.
35953 //if(!this.collapsed){
35956 // this.collapsedEl.show();
35958 this.visible = true;
35959 this.fireEvent("visibilitychange", this, true);
35962 closeClicked : function(){
35963 if(this.activePanel){
35964 this.remove(this.activePanel);
35968 collapseClick : function(e){
35970 e.stopPropagation();
35973 e.stopPropagation();
35979 * Collapses this region.
35980 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35983 collapse : function(skipAnim, skipCheck = false){
35984 if(this.collapsed) {
35988 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35990 this.collapsed = true;
35992 this.split.el.hide();
35994 if(this.config.animate && skipAnim !== true){
35995 this.fireEvent("invalidated", this);
35996 this.animateCollapse();
35998 this.el.setLocation(-20000,-20000);
36000 this.collapsedEl.show();
36001 this.fireEvent("collapsed", this);
36002 this.fireEvent("invalidated", this);
36008 animateCollapse : function(){
36013 * Expands this region if it was previously collapsed.
36014 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36015 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36018 expand : function(e, skipAnim){
36020 e.stopPropagation();
36022 if(!this.collapsed || this.el.hasActiveFx()) {
36026 this.afterSlideIn();
36029 this.collapsed = false;
36030 if(this.config.animate && skipAnim !== true){
36031 this.animateExpand();
36035 this.split.el.show();
36037 this.collapsedEl.setLocation(-2000,-2000);
36038 this.collapsedEl.hide();
36039 this.fireEvent("invalidated", this);
36040 this.fireEvent("expanded", this);
36044 animateExpand : function(){
36048 initTabs : function()
36050 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36052 var ts = new Roo.bootstrap.panel.Tabs({
36053 el: this.bodyEl.dom,
36054 tabPosition: this.bottomTabs ? 'bottom' : 'top',
36055 disableTooltips: this.config.disableTabTips,
36056 toolbar : this.config.toolbar
36059 if(this.config.hideTabs){
36060 ts.stripWrap.setDisplayed(false);
36063 ts.resizeTabs = this.config.resizeTabs === true;
36064 ts.minTabWidth = this.config.minTabWidth || 40;
36065 ts.maxTabWidth = this.config.maxTabWidth || 250;
36066 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36067 ts.monitorResize = false;
36068 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36069 ts.bodyEl.addClass('roo-layout-tabs-body');
36070 this.panels.each(this.initPanelAsTab, this);
36073 initPanelAsTab : function(panel){
36074 var ti = this.tabs.addTab(
36078 this.config.closeOnTab && panel.isClosable(),
36081 if(panel.tabTip !== undefined){
36082 ti.setTooltip(panel.tabTip);
36084 ti.on("activate", function(){
36085 this.setActivePanel(panel);
36088 if(this.config.closeOnTab){
36089 ti.on("beforeclose", function(t, e){
36091 this.remove(panel);
36095 panel.tabItem = ti;
36100 updatePanelTitle : function(panel, title)
36102 if(this.activePanel == panel){
36103 this.updateTitle(title);
36106 var ti = this.tabs.getTab(panel.getEl().id);
36108 if(panel.tabTip !== undefined){
36109 ti.setTooltip(panel.tabTip);
36114 updateTitle : function(title){
36115 if(this.titleTextEl && !this.config.title){
36116 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
36120 setActivePanel : function(panel)
36122 panel = this.getPanel(panel);
36123 if(this.activePanel && this.activePanel != panel){
36124 if(this.activePanel.setActiveState(false) === false){
36128 this.activePanel = panel;
36129 panel.setActiveState(true);
36130 if(this.panelSize){
36131 panel.setSize(this.panelSize.width, this.panelSize.height);
36134 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36136 this.updateTitle(panel.getTitle());
36138 this.fireEvent("invalidated", this);
36140 this.fireEvent("panelactivated", this, panel);
36144 * Shows the specified panel.
36145 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36146 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36148 showPanel : function(panel)
36150 panel = this.getPanel(panel);
36153 var tab = this.tabs.getTab(panel.getEl().id);
36154 if(tab.isHidden()){
36155 this.tabs.unhideTab(tab.id);
36159 this.setActivePanel(panel);
36166 * Get the active panel for this region.
36167 * @return {Roo.ContentPanel} The active panel or null
36169 getActivePanel : function(){
36170 return this.activePanel;
36173 validateVisibility : function(){
36174 if(this.panels.getCount() < 1){
36175 this.updateTitle(" ");
36176 this.closeBtn.hide();
36179 if(!this.isVisible()){
36186 * Adds the passed ContentPanel(s) to this region.
36187 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36188 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36190 add : function(panel)
36192 if(arguments.length > 1){
36193 for(var i = 0, len = arguments.length; i < len; i++) {
36194 this.add(arguments[i]);
36199 // if we have not been rendered yet, then we can not really do much of this..
36200 if (!this.bodyEl) {
36201 this.unrendered_panels.push(panel);
36208 if(this.hasPanel(panel)){
36209 this.showPanel(panel);
36212 panel.setRegion(this);
36213 this.panels.add(panel);
36214 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36215 // sinle panel - no tab...?? would it not be better to render it with the tabs,
36216 // and hide them... ???
36217 this.bodyEl.dom.appendChild(panel.getEl().dom);
36218 if(panel.background !== true){
36219 this.setActivePanel(panel);
36221 this.fireEvent("paneladded", this, panel);
36228 this.initPanelAsTab(panel);
36232 if(panel.background !== true){
36233 this.tabs.activate(panel.getEl().id);
36235 this.fireEvent("paneladded", this, panel);
36240 * Hides the tab for the specified panel.
36241 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36243 hidePanel : function(panel){
36244 if(this.tabs && (panel = this.getPanel(panel))){
36245 this.tabs.hideTab(panel.getEl().id);
36250 * Unhides the tab for a previously hidden panel.
36251 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36253 unhidePanel : function(panel){
36254 if(this.tabs && (panel = this.getPanel(panel))){
36255 this.tabs.unhideTab(panel.getEl().id);
36259 clearPanels : function(){
36260 while(this.panels.getCount() > 0){
36261 this.remove(this.panels.first());
36266 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36267 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36268 * @param {Boolean} preservePanel Overrides the config preservePanel option
36269 * @return {Roo.ContentPanel} The panel that was removed
36271 remove : function(panel, preservePanel)
36273 panel = this.getPanel(panel);
36278 this.fireEvent("beforeremove", this, panel, e);
36279 if(e.cancel === true){
36282 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36283 var panelId = panel.getId();
36284 this.panels.removeKey(panelId);
36286 document.body.appendChild(panel.getEl().dom);
36289 this.tabs.removeTab(panel.getEl().id);
36290 }else if (!preservePanel){
36291 this.bodyEl.dom.removeChild(panel.getEl().dom);
36293 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36294 var p = this.panels.first();
36295 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36296 tempEl.appendChild(p.getEl().dom);
36297 this.bodyEl.update("");
36298 this.bodyEl.dom.appendChild(p.getEl().dom);
36300 this.updateTitle(p.getTitle());
36302 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36303 this.setActivePanel(p);
36305 panel.setRegion(null);
36306 if(this.activePanel == panel){
36307 this.activePanel = null;
36309 if(this.config.autoDestroy !== false && preservePanel !== true){
36310 try{panel.destroy();}catch(e){}
36312 this.fireEvent("panelremoved", this, panel);
36317 * Returns the TabPanel component used by this region
36318 * @return {Roo.TabPanel}
36320 getTabs : function(){
36324 createTool : function(parentEl, className){
36325 var btn = Roo.DomHelper.append(parentEl, {
36327 cls: "x-layout-tools-button",
36330 cls: "roo-layout-tools-button-inner " + className,
36334 btn.addClassOnOver("roo-layout-tools-button-over");
36339 * Ext JS Library 1.1.1
36340 * Copyright(c) 2006-2007, Ext JS, LLC.
36342 * Originally Released Under LGPL - original licence link has changed is not relivant.
36345 * <script type="text/javascript">
36351 * @class Roo.SplitLayoutRegion
36352 * @extends Roo.LayoutRegion
36353 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36355 Roo.bootstrap.layout.Split = function(config){
36356 this.cursor = config.cursor;
36357 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36360 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36362 splitTip : "Drag to resize.",
36363 collapsibleSplitTip : "Drag to resize. Double click to hide.",
36364 useSplitTips : false,
36366 applyConfig : function(config){
36367 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36370 onRender : function(ctr,pos) {
36372 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36373 if(!this.config.split){
36378 var splitEl = Roo.DomHelper.append(ctr.dom, {
36380 id: this.el.id + "-split",
36381 cls: "roo-layout-split roo-layout-split-"+this.position,
36384 /** The SplitBar for this region
36385 * @type Roo.SplitBar */
36386 // does not exist yet...
36387 Roo.log([this.position, this.orientation]);
36389 this.split = new Roo.bootstrap.SplitBar({
36390 dragElement : splitEl,
36391 resizingElement: this.el,
36392 orientation : this.orientation
36395 this.split.on("moved", this.onSplitMove, this);
36396 this.split.useShim = this.config.useShim === true;
36397 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36398 if(this.useSplitTips){
36399 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36401 //if(config.collapsible){
36402 // this.split.el.on("dblclick", this.collapse, this);
36405 if(typeof this.config.minSize != "undefined"){
36406 this.split.minSize = this.config.minSize;
36408 if(typeof this.config.maxSize != "undefined"){
36409 this.split.maxSize = this.config.maxSize;
36411 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36412 this.hideSplitter();
36417 getHMaxSize : function(){
36418 var cmax = this.config.maxSize || 10000;
36419 var center = this.mgr.getRegion("center");
36420 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36423 getVMaxSize : function(){
36424 var cmax = this.config.maxSize || 10000;
36425 var center = this.mgr.getRegion("center");
36426 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36429 onSplitMove : function(split, newSize){
36430 this.fireEvent("resized", this, newSize);
36434 * Returns the {@link Roo.SplitBar} for this region.
36435 * @return {Roo.SplitBar}
36437 getSplitBar : function(){
36442 this.hideSplitter();
36443 Roo.bootstrap.layout.Split.superclass.hide.call(this);
36446 hideSplitter : function(){
36448 this.split.el.setLocation(-2000,-2000);
36449 this.split.el.hide();
36455 this.split.el.show();
36457 Roo.bootstrap.layout.Split.superclass.show.call(this);
36460 beforeSlide: function(){
36461 if(Roo.isGecko){// firefox overflow auto bug workaround
36462 this.bodyEl.clip();
36464 this.tabs.bodyEl.clip();
36466 if(this.activePanel){
36467 this.activePanel.getEl().clip();
36469 if(this.activePanel.beforeSlide){
36470 this.activePanel.beforeSlide();
36476 afterSlide : function(){
36477 if(Roo.isGecko){// firefox overflow auto bug workaround
36478 this.bodyEl.unclip();
36480 this.tabs.bodyEl.unclip();
36482 if(this.activePanel){
36483 this.activePanel.getEl().unclip();
36484 if(this.activePanel.afterSlide){
36485 this.activePanel.afterSlide();
36491 initAutoHide : function(){
36492 if(this.autoHide !== false){
36493 if(!this.autoHideHd){
36494 var st = new Roo.util.DelayedTask(this.slideIn, this);
36495 this.autoHideHd = {
36496 "mouseout": function(e){
36497 if(!e.within(this.el, true)){
36501 "mouseover" : function(e){
36507 this.el.on(this.autoHideHd);
36511 clearAutoHide : function(){
36512 if(this.autoHide !== false){
36513 this.el.un("mouseout", this.autoHideHd.mouseout);
36514 this.el.un("mouseover", this.autoHideHd.mouseover);
36518 clearMonitor : function(){
36519 Roo.get(document).un("click", this.slideInIf, this);
36522 // these names are backwards but not changed for compat
36523 slideOut : function(){
36524 if(this.isSlid || this.el.hasActiveFx()){
36527 this.isSlid = true;
36528 if(this.collapseBtn){
36529 this.collapseBtn.hide();
36531 this.closeBtnState = this.closeBtn.getStyle('display');
36532 this.closeBtn.hide();
36534 this.stickBtn.show();
36537 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36538 this.beforeSlide();
36539 this.el.setStyle("z-index", 10001);
36540 this.el.slideIn(this.getSlideAnchor(), {
36541 callback: function(){
36543 this.initAutoHide();
36544 Roo.get(document).on("click", this.slideInIf, this);
36545 this.fireEvent("slideshow", this);
36552 afterSlideIn : function(){
36553 this.clearAutoHide();
36554 this.isSlid = false;
36555 this.clearMonitor();
36556 this.el.setStyle("z-index", "");
36557 if(this.collapseBtn){
36558 this.collapseBtn.show();
36560 this.closeBtn.setStyle('display', this.closeBtnState);
36562 this.stickBtn.hide();
36564 this.fireEvent("slidehide", this);
36567 slideIn : function(cb){
36568 if(!this.isSlid || this.el.hasActiveFx()){
36572 this.isSlid = false;
36573 this.beforeSlide();
36574 this.el.slideOut(this.getSlideAnchor(), {
36575 callback: function(){
36576 this.el.setLeftTop(-10000, -10000);
36578 this.afterSlideIn();
36586 slideInIf : function(e){
36587 if(!e.within(this.el)){
36592 animateCollapse : function(){
36593 this.beforeSlide();
36594 this.el.setStyle("z-index", 20000);
36595 var anchor = this.getSlideAnchor();
36596 this.el.slideOut(anchor, {
36597 callback : function(){
36598 this.el.setStyle("z-index", "");
36599 this.collapsedEl.slideIn(anchor, {duration:.3});
36601 this.el.setLocation(-10000,-10000);
36603 this.fireEvent("collapsed", this);
36610 animateExpand : function(){
36611 this.beforeSlide();
36612 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36613 this.el.setStyle("z-index", 20000);
36614 this.collapsedEl.hide({
36617 this.el.slideIn(this.getSlideAnchor(), {
36618 callback : function(){
36619 this.el.setStyle("z-index", "");
36622 this.split.el.show();
36624 this.fireEvent("invalidated", this);
36625 this.fireEvent("expanded", this);
36653 getAnchor : function(){
36654 return this.anchors[this.position];
36657 getCollapseAnchor : function(){
36658 return this.canchors[this.position];
36661 getSlideAnchor : function(){
36662 return this.sanchors[this.position];
36665 getAlignAdj : function(){
36666 var cm = this.cmargins;
36667 switch(this.position){
36683 getExpandAdj : function(){
36684 var c = this.collapsedEl, cm = this.cmargins;
36685 switch(this.position){
36687 return [-(cm.right+c.getWidth()+cm.left), 0];
36690 return [cm.right+c.getWidth()+cm.left, 0];
36693 return [0, -(cm.top+cm.bottom+c.getHeight())];
36696 return [0, cm.top+cm.bottom+c.getHeight()];
36702 * Ext JS Library 1.1.1
36703 * Copyright(c) 2006-2007, Ext JS, LLC.
36705 * Originally Released Under LGPL - original licence link has changed is not relivant.
36708 * <script type="text/javascript">
36711 * These classes are private internal classes
36713 Roo.bootstrap.layout.Center = function(config){
36714 config.region = "center";
36715 Roo.bootstrap.layout.Region.call(this, config);
36716 this.visible = true;
36717 this.minWidth = config.minWidth || 20;
36718 this.minHeight = config.minHeight || 20;
36721 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36723 // center panel can't be hidden
36727 // center panel can't be hidden
36730 getMinWidth: function(){
36731 return this.minWidth;
36734 getMinHeight: function(){
36735 return this.minHeight;
36748 Roo.bootstrap.layout.North = function(config)
36750 config.region = 'north';
36751 config.cursor = 'n-resize';
36753 Roo.bootstrap.layout.Split.call(this, config);
36757 this.split.placement = Roo.bootstrap.SplitBar.TOP;
36758 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36759 this.split.el.addClass("roo-layout-split-v");
36761 var size = config.initialSize || config.height;
36762 if(typeof size != "undefined"){
36763 this.el.setHeight(size);
36766 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36768 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36772 getBox : function(){
36773 if(this.collapsed){
36774 return this.collapsedEl.getBox();
36776 var box = this.el.getBox();
36778 box.height += this.split.el.getHeight();
36783 updateBox : function(box){
36784 if(this.split && !this.collapsed){
36785 box.height -= this.split.el.getHeight();
36786 this.split.el.setLeft(box.x);
36787 this.split.el.setTop(box.y+box.height);
36788 this.split.el.setWidth(box.width);
36790 if(this.collapsed){
36791 this.updateBody(box.width, null);
36793 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36801 Roo.bootstrap.layout.South = function(config){
36802 config.region = 'south';
36803 config.cursor = 's-resize';
36804 Roo.bootstrap.layout.Split.call(this, config);
36806 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36807 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36808 this.split.el.addClass("roo-layout-split-v");
36810 var size = config.initialSize || config.height;
36811 if(typeof size != "undefined"){
36812 this.el.setHeight(size);
36816 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36817 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36818 getBox : function(){
36819 if(this.collapsed){
36820 return this.collapsedEl.getBox();
36822 var box = this.el.getBox();
36824 var sh = this.split.el.getHeight();
36831 updateBox : function(box){
36832 if(this.split && !this.collapsed){
36833 var sh = this.split.el.getHeight();
36836 this.split.el.setLeft(box.x);
36837 this.split.el.setTop(box.y-sh);
36838 this.split.el.setWidth(box.width);
36840 if(this.collapsed){
36841 this.updateBody(box.width, null);
36843 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36847 Roo.bootstrap.layout.East = function(config){
36848 config.region = "east";
36849 config.cursor = "e-resize";
36850 Roo.bootstrap.layout.Split.call(this, config);
36852 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36853 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36854 this.split.el.addClass("roo-layout-split-h");
36856 var size = config.initialSize || config.width;
36857 if(typeof size != "undefined"){
36858 this.el.setWidth(size);
36861 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36862 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36863 getBox : function(){
36864 if(this.collapsed){
36865 return this.collapsedEl.getBox();
36867 var box = this.el.getBox();
36869 var sw = this.split.el.getWidth();
36876 updateBox : function(box){
36877 if(this.split && !this.collapsed){
36878 var sw = this.split.el.getWidth();
36880 this.split.el.setLeft(box.x);
36881 this.split.el.setTop(box.y);
36882 this.split.el.setHeight(box.height);
36885 if(this.collapsed){
36886 this.updateBody(null, box.height);
36888 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36892 Roo.bootstrap.layout.West = function(config){
36893 config.region = "west";
36894 config.cursor = "w-resize";
36896 Roo.bootstrap.layout.Split.call(this, config);
36898 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36899 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36900 this.split.el.addClass("roo-layout-split-h");
36904 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36905 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36907 onRender: function(ctr, pos)
36909 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36910 var size = this.config.initialSize || this.config.width;
36911 if(typeof size != "undefined"){
36912 this.el.setWidth(size);
36916 getBox : function(){
36917 if(this.collapsed){
36918 return this.collapsedEl.getBox();
36920 var box = this.el.getBox();
36922 box.width += this.split.el.getWidth();
36927 updateBox : function(box){
36928 if(this.split && !this.collapsed){
36929 var sw = this.split.el.getWidth();
36931 this.split.el.setLeft(box.x+box.width);
36932 this.split.el.setTop(box.y);
36933 this.split.el.setHeight(box.height);
36935 if(this.collapsed){
36936 this.updateBody(null, box.height);
36938 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36941 Roo.namespace("Roo.bootstrap.panel");/*
36943 * Ext JS Library 1.1.1
36944 * Copyright(c) 2006-2007, Ext JS, LLC.
36946 * Originally Released Under LGPL - original licence link has changed is not relivant.
36949 * <script type="text/javascript">
36952 * @class Roo.ContentPanel
36953 * @extends Roo.util.Observable
36954 * A basic ContentPanel element.
36955 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
36956 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
36957 * @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
36958 * @cfg {Boolean} closable True if the panel can be closed/removed
36959 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
36960 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36961 * @cfg {Toolbar} toolbar A toolbar for this panel
36962 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
36963 * @cfg {String} title The title for this panel
36964 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36965 * @cfg {String} url Calls {@link #setUrl} with this value
36966 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36967 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
36968 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
36969 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
36970 * @cfg {Boolean} badges render the badges
36973 * Create a new ContentPanel.
36974 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36975 * @param {String/Object} config A string to set only the title or a config object
36976 * @param {String} content (optional) Set the HTML content for this panel
36977 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36979 Roo.bootstrap.panel.Content = function( config){
36981 this.tpl = config.tpl || false;
36983 var el = config.el;
36984 var content = config.content;
36986 if(config.autoCreate){ // xtype is available if this is called from factory
36989 this.el = Roo.get(el);
36990 if(!this.el && config && config.autoCreate){
36991 if(typeof config.autoCreate == "object"){
36992 if(!config.autoCreate.id){
36993 config.autoCreate.id = config.id||el;
36995 this.el = Roo.DomHelper.append(document.body,
36996 config.autoCreate, true);
36998 var elcfg = { tag: "div",
36999 cls: "roo-layout-inactive-content",
37003 elcfg.html = config.html;
37007 this.el = Roo.DomHelper.append(document.body, elcfg , true);
37010 this.closable = false;
37011 this.loaded = false;
37012 this.active = false;
37015 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37017 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37019 this.wrapEl = this.el; //this.el.wrap();
37021 if (config.toolbar.items) {
37022 ti = config.toolbar.items ;
37023 delete config.toolbar.items ;
37027 this.toolbar.render(this.wrapEl, 'before');
37028 for(var i =0;i < ti.length;i++) {
37029 // Roo.log(['add child', items[i]]);
37030 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37032 this.toolbar.items = nitems;
37033 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37034 delete config.toolbar;
37038 // xtype created footer. - not sure if will work as we normally have to render first..
37039 if (this.footer && !this.footer.el && this.footer.xtype) {
37040 if (!this.wrapEl) {
37041 this.wrapEl = this.el.wrap();
37044 this.footer.container = this.wrapEl.createChild();
37046 this.footer = Roo.factory(this.footer, Roo);
37051 if(typeof config == "string"){
37052 this.title = config;
37054 Roo.apply(this, config);
37058 this.resizeEl = Roo.get(this.resizeEl, true);
37060 this.resizeEl = this.el;
37062 // handle view.xtype
37070 * Fires when this panel is activated.
37071 * @param {Roo.ContentPanel} this
37075 * @event deactivate
37076 * Fires when this panel is activated.
37077 * @param {Roo.ContentPanel} this
37079 "deactivate" : true,
37083 * Fires when this panel is resized if fitToFrame is true.
37084 * @param {Roo.ContentPanel} this
37085 * @param {Number} width The width after any component adjustments
37086 * @param {Number} height The height after any component adjustments
37092 * Fires when this tab is created
37093 * @param {Roo.ContentPanel} this
37104 if(this.autoScroll){
37105 this.resizeEl.setStyle("overflow", "auto");
37107 // fix randome scrolling
37108 //this.el.on('scroll', function() {
37109 // Roo.log('fix random scolling');
37110 // this.scrollTo('top',0);
37113 content = content || this.content;
37115 this.setContent(content);
37117 if(config && config.url){
37118 this.setUrl(this.url, this.params, this.loadOnce);
37123 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37125 if (this.view && typeof(this.view.xtype) != 'undefined') {
37126 this.view.el = this.el.appendChild(document.createElement("div"));
37127 this.view = Roo.factory(this.view);
37128 this.view.render && this.view.render(false, '');
37132 this.fireEvent('render', this);
37135 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37139 setRegion : function(region){
37140 this.region = region;
37141 this.setActiveClass(region && !this.background);
37145 setActiveClass: function(state)
37148 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37149 this.el.setStyle('position','relative');
37151 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37152 this.el.setStyle('position', 'absolute');
37157 * Returns the toolbar for this Panel if one was configured.
37158 * @return {Roo.Toolbar}
37160 getToolbar : function(){
37161 return this.toolbar;
37164 setActiveState : function(active)
37166 this.active = active;
37167 this.setActiveClass(active);
37169 if(this.fireEvent("deactivate", this) === false){
37174 this.fireEvent("activate", this);
37178 * Updates this panel's element
37179 * @param {String} content The new content
37180 * @param {Boolean} loadScripts (optional) true to look for and process scripts
37182 setContent : function(content, loadScripts){
37183 this.el.update(content, loadScripts);
37186 ignoreResize : function(w, h){
37187 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37190 this.lastSize = {width: w, height: h};
37195 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37196 * @return {Roo.UpdateManager} The UpdateManager
37198 getUpdateManager : function(){
37199 return this.el.getUpdateManager();
37202 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37203 * @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:
37206 url: "your-url.php",
37207 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37208 callback: yourFunction,
37209 scope: yourObject, //(optional scope)
37212 text: "Loading...",
37217 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37218 * 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.
37219 * @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}
37220 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37221 * @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.
37222 * @return {Roo.ContentPanel} this
37225 var um = this.el.getUpdateManager();
37226 um.update.apply(um, arguments);
37232 * 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.
37233 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37234 * @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)
37235 * @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)
37236 * @return {Roo.UpdateManager} The UpdateManager
37238 setUrl : function(url, params, loadOnce){
37239 if(this.refreshDelegate){
37240 this.removeListener("activate", this.refreshDelegate);
37242 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37243 this.on("activate", this.refreshDelegate);
37244 return this.el.getUpdateManager();
37247 _handleRefresh : function(url, params, loadOnce){
37248 if(!loadOnce || !this.loaded){
37249 var updater = this.el.getUpdateManager();
37250 updater.update(url, params, this._setLoaded.createDelegate(this));
37254 _setLoaded : function(){
37255 this.loaded = true;
37259 * Returns this panel's id
37262 getId : function(){
37267 * Returns this panel's element - used by regiosn to add.
37268 * @return {Roo.Element}
37270 getEl : function(){
37271 return this.wrapEl || this.el;
37276 adjustForComponents : function(width, height)
37278 //Roo.log('adjustForComponents ');
37279 if(this.resizeEl != this.el){
37280 width -= this.el.getFrameWidth('lr');
37281 height -= this.el.getFrameWidth('tb');
37284 var te = this.toolbar.getEl();
37285 te.setWidth(width);
37286 height -= te.getHeight();
37289 var te = this.footer.getEl();
37290 te.setWidth(width);
37291 height -= te.getHeight();
37295 if(this.adjustments){
37296 width += this.adjustments[0];
37297 height += this.adjustments[1];
37299 return {"width": width, "height": height};
37302 setSize : function(width, height){
37303 if(this.fitToFrame && !this.ignoreResize(width, height)){
37304 if(this.fitContainer && this.resizeEl != this.el){
37305 this.el.setSize(width, height);
37307 var size = this.adjustForComponents(width, height);
37308 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37309 this.fireEvent('resize', this, size.width, size.height);
37314 * Returns this panel's title
37317 getTitle : function(){
37319 if (typeof(this.title) != 'object') {
37324 for (var k in this.title) {
37325 if (!this.title.hasOwnProperty(k)) {
37329 if (k.indexOf('-') >= 0) {
37330 var s = k.split('-');
37331 for (var i = 0; i<s.length; i++) {
37332 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37335 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37342 * Set this panel's title
37343 * @param {String} title
37345 setTitle : function(title){
37346 this.title = title;
37348 this.region.updatePanelTitle(this, title);
37353 * Returns true is this panel was configured to be closable
37354 * @return {Boolean}
37356 isClosable : function(){
37357 return this.closable;
37360 beforeSlide : function(){
37362 this.resizeEl.clip();
37365 afterSlide : function(){
37367 this.resizeEl.unclip();
37371 * Force a content refresh from the URL specified in the {@link #setUrl} method.
37372 * Will fail silently if the {@link #setUrl} method has not been called.
37373 * This does not activate the panel, just updates its content.
37375 refresh : function(){
37376 if(this.refreshDelegate){
37377 this.loaded = false;
37378 this.refreshDelegate();
37383 * Destroys this panel
37385 destroy : function(){
37386 this.el.removeAllListeners();
37387 var tempEl = document.createElement("span");
37388 tempEl.appendChild(this.el.dom);
37389 tempEl.innerHTML = "";
37395 * form - if the content panel contains a form - this is a reference to it.
37396 * @type {Roo.form.Form}
37400 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37401 * This contains a reference to it.
37407 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37417 * @param {Object} cfg Xtype definition of item to add.
37421 getChildContainer: function () {
37422 return this.getEl();
37427 var ret = new Roo.factory(cfg);
37432 if (cfg.xtype.match(/^Form$/)) {
37435 //if (this.footer) {
37436 // el = this.footer.container.insertSibling(false, 'before');
37438 el = this.el.createChild();
37441 this.form = new Roo.form.Form(cfg);
37444 if ( this.form.allItems.length) {
37445 this.form.render(el.dom);
37449 // should only have one of theses..
37450 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37451 // views.. should not be just added - used named prop 'view''
37453 cfg.el = this.el.appendChild(document.createElement("div"));
37456 var ret = new Roo.factory(cfg);
37458 ret.render && ret.render(false, ''); // render blank..
37468 * @class Roo.bootstrap.panel.Grid
37469 * @extends Roo.bootstrap.panel.Content
37471 * Create a new GridPanel.
37472 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37473 * @param {Object} config A the config object
37479 Roo.bootstrap.panel.Grid = function(config)
37483 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37484 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37486 config.el = this.wrapper;
37487 //this.el = this.wrapper;
37489 if (config.container) {
37490 // ctor'ed from a Border/panel.grid
37493 this.wrapper.setStyle("overflow", "hidden");
37494 this.wrapper.addClass('roo-grid-container');
37499 if(config.toolbar){
37500 var tool_el = this.wrapper.createChild();
37501 this.toolbar = Roo.factory(config.toolbar);
37503 if (config.toolbar.items) {
37504 ti = config.toolbar.items ;
37505 delete config.toolbar.items ;
37509 this.toolbar.render(tool_el);
37510 for(var i =0;i < ti.length;i++) {
37511 // Roo.log(['add child', items[i]]);
37512 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37514 this.toolbar.items = nitems;
37516 delete config.toolbar;
37519 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37520 config.grid.scrollBody = true;;
37521 config.grid.monitorWindowResize = false; // turn off autosizing
37522 config.grid.autoHeight = false;
37523 config.grid.autoWidth = false;
37525 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37527 if (config.background) {
37528 // render grid on panel activation (if panel background)
37529 this.on('activate', function(gp) {
37530 if (!gp.grid.rendered) {
37531 gp.grid.render(this.wrapper);
37532 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37537 this.grid.render(this.wrapper);
37538 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37541 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37542 // ??? needed ??? config.el = this.wrapper;
37547 // xtype created footer. - not sure if will work as we normally have to render first..
37548 if (this.footer && !this.footer.el && this.footer.xtype) {
37550 var ctr = this.grid.getView().getFooterPanel(true);
37551 this.footer.dataSource = this.grid.dataSource;
37552 this.footer = Roo.factory(this.footer, Roo);
37553 this.footer.render(ctr);
37563 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37564 getId : function(){
37565 return this.grid.id;
37569 * Returns the grid for this panel
37570 * @return {Roo.bootstrap.Table}
37572 getGrid : function(){
37576 setSize : function(width, height){
37577 if(!this.ignoreResize(width, height)){
37578 var grid = this.grid;
37579 var size = this.adjustForComponents(width, height);
37580 var gridel = grid.getGridEl();
37581 gridel.setSize(size.width, size.height);
37583 var thd = grid.getGridEl().select('thead',true).first();
37584 var tbd = grid.getGridEl().select('tbody', true).first();
37586 tbd.setSize(width, height - thd.getHeight());
37595 beforeSlide : function(){
37596 this.grid.getView().scroller.clip();
37599 afterSlide : function(){
37600 this.grid.getView().scroller.unclip();
37603 destroy : function(){
37604 this.grid.destroy();
37606 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
37611 * @class Roo.bootstrap.panel.Nest
37612 * @extends Roo.bootstrap.panel.Content
37614 * Create a new Panel, that can contain a layout.Border.
37617 * @param {Roo.BorderLayout} layout The layout for this panel
37618 * @param {String/Object} config A string to set only the title or a config object
37620 Roo.bootstrap.panel.Nest = function(config)
37622 // construct with only one argument..
37623 /* FIXME - implement nicer consturctors
37624 if (layout.layout) {
37626 layout = config.layout;
37627 delete config.layout;
37629 if (layout.xtype && !layout.getEl) {
37630 // then layout needs constructing..
37631 layout = Roo.factory(layout, Roo);
37635 config.el = config.layout.getEl();
37637 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37639 config.layout.monitorWindowResize = false; // turn off autosizing
37640 this.layout = config.layout;
37641 this.layout.getEl().addClass("roo-layout-nested-layout");
37648 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37650 setSize : function(width, height){
37651 if(!this.ignoreResize(width, height)){
37652 var size = this.adjustForComponents(width, height);
37653 var el = this.layout.getEl();
37654 if (size.height < 1) {
37655 el.setWidth(size.width);
37657 el.setSize(size.width, size.height);
37659 var touch = el.dom.offsetWidth;
37660 this.layout.layout();
37661 // ie requires a double layout on the first pass
37662 if(Roo.isIE && !this.initialized){
37663 this.initialized = true;
37664 this.layout.layout();
37669 // activate all subpanels if not currently active..
37671 setActiveState : function(active){
37672 this.active = active;
37673 this.setActiveClass(active);
37676 this.fireEvent("deactivate", this);
37680 this.fireEvent("activate", this);
37681 // not sure if this should happen before or after..
37682 if (!this.layout) {
37683 return; // should not happen..
37686 for (var r in this.layout.regions) {
37687 reg = this.layout.getRegion(r);
37688 if (reg.getActivePanel()) {
37689 //reg.showPanel(reg.getActivePanel()); // force it to activate..
37690 reg.setActivePanel(reg.getActivePanel());
37693 if (!reg.panels.length) {
37696 reg.showPanel(reg.getPanel(0));
37705 * Returns the nested BorderLayout for this panel
37706 * @return {Roo.BorderLayout}
37708 getLayout : function(){
37709 return this.layout;
37713 * Adds a xtype elements to the layout of the nested panel
37717 xtype : 'ContentPanel',
37724 xtype : 'NestedLayoutPanel',
37730 items : [ ... list of content panels or nested layout panels.. ]
37734 * @param {Object} cfg Xtype definition of item to add.
37736 addxtype : function(cfg) {
37737 return this.layout.addxtype(cfg);
37742 * Ext JS Library 1.1.1
37743 * Copyright(c) 2006-2007, Ext JS, LLC.
37745 * Originally Released Under LGPL - original licence link has changed is not relivant.
37748 * <script type="text/javascript">
37751 * @class Roo.TabPanel
37752 * @extends Roo.util.Observable
37753 * A lightweight tab container.
37757 // basic tabs 1, built from existing content
37758 var tabs = new Roo.TabPanel("tabs1");
37759 tabs.addTab("script", "View Script");
37760 tabs.addTab("markup", "View Markup");
37761 tabs.activate("script");
37763 // more advanced tabs, built from javascript
37764 var jtabs = new Roo.TabPanel("jtabs");
37765 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37767 // set up the UpdateManager
37768 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37769 var updater = tab2.getUpdateManager();
37770 updater.setDefaultUrl("ajax1.htm");
37771 tab2.on('activate', updater.refresh, updater, true);
37773 // Use setUrl for Ajax loading
37774 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37775 tab3.setUrl("ajax2.htm", null, true);
37778 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37781 jtabs.activate("jtabs-1");
37784 * Create a new TabPanel.
37785 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37786 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37788 Roo.bootstrap.panel.Tabs = function(config){
37790 * The container element for this TabPanel.
37791 * @type Roo.Element
37793 this.el = Roo.get(config.el);
37796 if(typeof config == "boolean"){
37797 this.tabPosition = config ? "bottom" : "top";
37799 Roo.apply(this, config);
37803 if(this.tabPosition == "bottom"){
37804 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37805 this.el.addClass("roo-tabs-bottom");
37807 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37808 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37809 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37811 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37813 if(this.tabPosition != "bottom"){
37814 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37815 * @type Roo.Element
37817 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37818 this.el.addClass("roo-tabs-top");
37822 this.bodyEl.setStyle("position", "relative");
37824 this.active = null;
37825 this.activateDelegate = this.activate.createDelegate(this);
37830 * Fires when the active tab changes
37831 * @param {Roo.TabPanel} this
37832 * @param {Roo.TabPanelItem} activePanel The new active tab
37836 * @event beforetabchange
37837 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37838 * @param {Roo.TabPanel} this
37839 * @param {Object} e Set cancel to true on this object to cancel the tab change
37840 * @param {Roo.TabPanelItem} tab The tab being changed to
37842 "beforetabchange" : true
37845 Roo.EventManager.onWindowResize(this.onResize, this);
37846 this.cpad = this.el.getPadding("lr");
37847 this.hiddenCount = 0;
37850 // toolbar on the tabbar support...
37851 if (this.toolbar) {
37852 alert("no toolbar support yet");
37853 this.toolbar = false;
37855 var tcfg = this.toolbar;
37856 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
37857 this.toolbar = new Roo.Toolbar(tcfg);
37858 if (Roo.isSafari) {
37859 var tbl = tcfg.container.child('table', true);
37860 tbl.setAttribute('width', '100%');
37868 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37871 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37873 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37875 tabPosition : "top",
37877 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37879 currentTabWidth : 0,
37881 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37885 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37889 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37891 preferredTabWidth : 175,
37893 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37895 resizeTabs : false,
37897 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37899 monitorResize : true,
37901 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
37906 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37907 * @param {String} id The id of the div to use <b>or create</b>
37908 * @param {String} text The text for the tab
37909 * @param {String} content (optional) Content to put in the TabPanelItem body
37910 * @param {Boolean} closable (optional) True to create a close icon on the tab
37911 * @return {Roo.TabPanelItem} The created TabPanelItem
37913 addTab : function(id, text, content, closable, tpl)
37915 var item = new Roo.bootstrap.panel.TabItem({
37919 closable : closable,
37922 this.addTabItem(item);
37924 item.setContent(content);
37930 * Returns the {@link Roo.TabPanelItem} with the specified id/index
37931 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37932 * @return {Roo.TabPanelItem}
37934 getTab : function(id){
37935 return this.items[id];
37939 * Hides the {@link Roo.TabPanelItem} with the specified id/index
37940 * @param {String/Number} id The id or index of the TabPanelItem to hide.
37942 hideTab : function(id){
37943 var t = this.items[id];
37946 this.hiddenCount++;
37947 this.autoSizeTabs();
37952 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37953 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37955 unhideTab : function(id){
37956 var t = this.items[id];
37958 t.setHidden(false);
37959 this.hiddenCount--;
37960 this.autoSizeTabs();
37965 * Adds an existing {@link Roo.TabPanelItem}.
37966 * @param {Roo.TabPanelItem} item The TabPanelItem to add
37968 addTabItem : function(item){
37969 this.items[item.id] = item;
37970 this.items.push(item);
37971 // if(this.resizeTabs){
37972 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37973 // this.autoSizeTabs();
37975 // item.autoSize();
37980 * Removes a {@link Roo.TabPanelItem}.
37981 * @param {String/Number} id The id or index of the TabPanelItem to remove.
37983 removeTab : function(id){
37984 var items = this.items;
37985 var tab = items[id];
37986 if(!tab) { return; }
37987 var index = items.indexOf(tab);
37988 if(this.active == tab && items.length > 1){
37989 var newTab = this.getNextAvailable(index);
37994 this.stripEl.dom.removeChild(tab.pnode.dom);
37995 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37996 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37998 items.splice(index, 1);
37999 delete this.items[tab.id];
38000 tab.fireEvent("close", tab);
38001 tab.purgeListeners();
38002 this.autoSizeTabs();
38005 getNextAvailable : function(start){
38006 var items = this.items;
38008 // look for a next tab that will slide over to
38009 // replace the one being removed
38010 while(index < items.length){
38011 var item = items[++index];
38012 if(item && !item.isHidden()){
38016 // if one isn't found select the previous tab (on the left)
38019 var item = items[--index];
38020 if(item && !item.isHidden()){
38028 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38029 * @param {String/Number} id The id or index of the TabPanelItem to disable.
38031 disableTab : function(id){
38032 var tab = this.items[id];
38033 if(tab && this.active != tab){
38039 * Enables a {@link Roo.TabPanelItem} that is disabled.
38040 * @param {String/Number} id The id or index of the TabPanelItem to enable.
38042 enableTab : function(id){
38043 var tab = this.items[id];
38048 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38049 * @param {String/Number} id The id or index of the TabPanelItem to activate.
38050 * @return {Roo.TabPanelItem} The TabPanelItem.
38052 activate : function(id){
38053 var tab = this.items[id];
38057 if(tab == this.active || tab.disabled){
38061 this.fireEvent("beforetabchange", this, e, tab);
38062 if(e.cancel !== true && !tab.disabled){
38064 this.active.hide();
38066 this.active = this.items[id];
38067 this.active.show();
38068 this.fireEvent("tabchange", this, this.active);
38074 * Gets the active {@link Roo.TabPanelItem}.
38075 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38077 getActiveTab : function(){
38078 return this.active;
38082 * Updates the tab body element to fit the height of the container element
38083 * for overflow scrolling
38084 * @param {Number} targetHeight (optional) Override the starting height from the elements height
38086 syncHeight : function(targetHeight){
38087 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38088 var bm = this.bodyEl.getMargins();
38089 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38090 this.bodyEl.setHeight(newHeight);
38094 onResize : function(){
38095 if(this.monitorResize){
38096 this.autoSizeTabs();
38101 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38103 beginUpdate : function(){
38104 this.updating = true;
38108 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38110 endUpdate : function(){
38111 this.updating = false;
38112 this.autoSizeTabs();
38116 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38118 autoSizeTabs : function(){
38119 var count = this.items.length;
38120 var vcount = count - this.hiddenCount;
38121 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38124 var w = Math.max(this.el.getWidth() - this.cpad, 10);
38125 var availWidth = Math.floor(w / vcount);
38126 var b = this.stripBody;
38127 if(b.getWidth() > w){
38128 var tabs = this.items;
38129 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38130 if(availWidth < this.minTabWidth){
38131 /*if(!this.sleft){ // incomplete scrolling code
38132 this.createScrollButtons();
38135 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38138 if(this.currentTabWidth < this.preferredTabWidth){
38139 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38145 * Returns the number of tabs in this TabPanel.
38148 getCount : function(){
38149 return this.items.length;
38153 * Resizes all the tabs to the passed width
38154 * @param {Number} The new width
38156 setTabWidth : function(width){
38157 this.currentTabWidth = width;
38158 for(var i = 0, len = this.items.length; i < len; i++) {
38159 if(!this.items[i].isHidden()) {
38160 this.items[i].setWidth(width);
38166 * Destroys this TabPanel
38167 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38169 destroy : function(removeEl){
38170 Roo.EventManager.removeResizeListener(this.onResize, this);
38171 for(var i = 0, len = this.items.length; i < len; i++){
38172 this.items[i].purgeListeners();
38174 if(removeEl === true){
38175 this.el.update("");
38180 createStrip : function(container)
38182 var strip = document.createElement("nav");
38183 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38184 container.appendChild(strip);
38188 createStripList : function(strip)
38190 // div wrapper for retard IE
38191 // returns the "tr" element.
38192 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38193 //'<div class="x-tabs-strip-wrap">'+
38194 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38195 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38196 return strip.firstChild; //.firstChild.firstChild.firstChild;
38198 createBody : function(container)
38200 var body = document.createElement("div");
38201 Roo.id(body, "tab-body");
38202 //Roo.fly(body).addClass("x-tabs-body");
38203 Roo.fly(body).addClass("tab-content");
38204 container.appendChild(body);
38207 createItemBody :function(bodyEl, id){
38208 var body = Roo.getDom(id);
38210 body = document.createElement("div");
38213 //Roo.fly(body).addClass("x-tabs-item-body");
38214 Roo.fly(body).addClass("tab-pane");
38215 bodyEl.insertBefore(body, bodyEl.firstChild);
38219 createStripElements : function(stripEl, text, closable, tpl)
38221 var td = document.createElement("li"); // was td..
38224 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38227 stripEl.appendChild(td);
38229 td.className = "x-tabs-closable";
38230 if(!this.closeTpl){
38231 this.closeTpl = new Roo.Template(
38232 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38233 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38234 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
38237 var el = this.closeTpl.overwrite(td, {"text": text});
38238 var close = el.getElementsByTagName("div")[0];
38239 var inner = el.getElementsByTagName("em")[0];
38240 return {"el": el, "close": close, "inner": inner};
38243 // not sure what this is..
38244 // if(!this.tabTpl){
38245 //this.tabTpl = new Roo.Template(
38246 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38247 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38249 // this.tabTpl = new Roo.Template(
38250 // '<a href="#">' +
38251 // '<span unselectable="on"' +
38252 // (this.disableTooltips ? '' : ' title="{text}"') +
38253 // ' >{text}</span></a>'
38259 var template = tpl || this.tabTpl || false;
38263 template = new Roo.Template(
38265 '<span unselectable="on"' +
38266 (this.disableTooltips ? '' : ' title="{text}"') +
38267 ' >{text}</span></a>'
38271 switch (typeof(template)) {
38275 template = new Roo.Template(template);
38281 var el = template.overwrite(td, {"text": text});
38283 var inner = el.getElementsByTagName("span")[0];
38285 return {"el": el, "inner": inner};
38293 * @class Roo.TabPanelItem
38294 * @extends Roo.util.Observable
38295 * Represents an individual item (tab plus body) in a TabPanel.
38296 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38297 * @param {String} id The id of this TabPanelItem
38298 * @param {String} text The text for the tab of this TabPanelItem
38299 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38301 Roo.bootstrap.panel.TabItem = function(config){
38303 * The {@link Roo.TabPanel} this TabPanelItem belongs to
38304 * @type Roo.TabPanel
38306 this.tabPanel = config.panel;
38308 * The id for this TabPanelItem
38311 this.id = config.id;
38313 this.disabled = false;
38315 this.text = config.text;
38317 this.loaded = false;
38318 this.closable = config.closable;
38321 * The body element for this TabPanelItem.
38322 * @type Roo.Element
38324 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38325 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38326 this.bodyEl.setStyle("display", "block");
38327 this.bodyEl.setStyle("zoom", "1");
38328 //this.hideAction();
38330 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38332 this.el = Roo.get(els.el);
38333 this.inner = Roo.get(els.inner, true);
38334 this.textEl = Roo.get(this.el.dom.firstChild, true);
38335 this.pnode = Roo.get(els.el.parentNode, true);
38336 // this.el.on("mousedown", this.onTabMouseDown, this);
38337 this.el.on("click", this.onTabClick, this);
38339 if(config.closable){
38340 var c = Roo.get(els.close, true);
38341 c.dom.title = this.closeText;
38342 c.addClassOnOver("close-over");
38343 c.on("click", this.closeClick, this);
38349 * Fires when this tab becomes the active tab.
38350 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38351 * @param {Roo.TabPanelItem} this
38355 * @event beforeclose
38356 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38357 * @param {Roo.TabPanelItem} this
38358 * @param {Object} e Set cancel to true on this object to cancel the close.
38360 "beforeclose": true,
38363 * Fires when this tab is closed.
38364 * @param {Roo.TabPanelItem} this
38368 * @event deactivate
38369 * Fires when this tab is no longer the active tab.
38370 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38371 * @param {Roo.TabPanelItem} this
38373 "deactivate" : true
38375 this.hidden = false;
38377 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38380 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38382 purgeListeners : function(){
38383 Roo.util.Observable.prototype.purgeListeners.call(this);
38384 this.el.removeAllListeners();
38387 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38390 this.pnode.addClass("active");
38393 this.tabPanel.stripWrap.repaint();
38395 this.fireEvent("activate", this.tabPanel, this);
38399 * Returns true if this tab is the active tab.
38400 * @return {Boolean}
38402 isActive : function(){
38403 return this.tabPanel.getActiveTab() == this;
38407 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38410 this.pnode.removeClass("active");
38412 this.fireEvent("deactivate", this.tabPanel, this);
38415 hideAction : function(){
38416 this.bodyEl.hide();
38417 this.bodyEl.setStyle("position", "absolute");
38418 this.bodyEl.setLeft("-20000px");
38419 this.bodyEl.setTop("-20000px");
38422 showAction : function(){
38423 this.bodyEl.setStyle("position", "relative");
38424 this.bodyEl.setTop("");
38425 this.bodyEl.setLeft("");
38426 this.bodyEl.show();
38430 * Set the tooltip for the tab.
38431 * @param {String} tooltip The tab's tooltip
38433 setTooltip : function(text){
38434 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38435 this.textEl.dom.qtip = text;
38436 this.textEl.dom.removeAttribute('title');
38438 this.textEl.dom.title = text;
38442 onTabClick : function(e){
38443 e.preventDefault();
38444 this.tabPanel.activate(this.id);
38447 onTabMouseDown : function(e){
38448 e.preventDefault();
38449 this.tabPanel.activate(this.id);
38452 getWidth : function(){
38453 return this.inner.getWidth();
38456 setWidth : function(width){
38457 var iwidth = width - this.pnode.getPadding("lr");
38458 this.inner.setWidth(iwidth);
38459 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38460 this.pnode.setWidth(width);
38464 * Show or hide the tab
38465 * @param {Boolean} hidden True to hide or false to show.
38467 setHidden : function(hidden){
38468 this.hidden = hidden;
38469 this.pnode.setStyle("display", hidden ? "none" : "");
38473 * Returns true if this tab is "hidden"
38474 * @return {Boolean}
38476 isHidden : function(){
38477 return this.hidden;
38481 * Returns the text for this tab
38484 getText : function(){
38488 autoSize : function(){
38489 //this.el.beginMeasure();
38490 this.textEl.setWidth(1);
38492 * #2804 [new] Tabs in Roojs
38493 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38495 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38496 //this.el.endMeasure();
38500 * Sets the text for the tab (Note: this also sets the tooltip text)
38501 * @param {String} text The tab's text and tooltip
38503 setText : function(text){
38505 this.textEl.update(text);
38506 this.setTooltip(text);
38507 //if(!this.tabPanel.resizeTabs){
38508 // this.autoSize();
38512 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38514 activate : function(){
38515 this.tabPanel.activate(this.id);
38519 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38521 disable : function(){
38522 if(this.tabPanel.active != this){
38523 this.disabled = true;
38524 this.pnode.addClass("disabled");
38529 * Enables this TabPanelItem if it was previously disabled.
38531 enable : function(){
38532 this.disabled = false;
38533 this.pnode.removeClass("disabled");
38537 * Sets the content for this TabPanelItem.
38538 * @param {String} content The content
38539 * @param {Boolean} loadScripts true to look for and load scripts
38541 setContent : function(content, loadScripts){
38542 this.bodyEl.update(content, loadScripts);
38546 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38547 * @return {Roo.UpdateManager} The UpdateManager
38549 getUpdateManager : function(){
38550 return this.bodyEl.getUpdateManager();
38554 * Set a URL to be used to load the content for this TabPanelItem.
38555 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38556 * @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)
38557 * @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)
38558 * @return {Roo.UpdateManager} The UpdateManager
38560 setUrl : function(url, params, loadOnce){
38561 if(this.refreshDelegate){
38562 this.un('activate', this.refreshDelegate);
38564 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38565 this.on("activate", this.refreshDelegate);
38566 return this.bodyEl.getUpdateManager();
38570 _handleRefresh : function(url, params, loadOnce){
38571 if(!loadOnce || !this.loaded){
38572 var updater = this.bodyEl.getUpdateManager();
38573 updater.update(url, params, this._setLoaded.createDelegate(this));
38578 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
38579 * Will fail silently if the setUrl method has not been called.
38580 * This does not activate the panel, just updates its content.
38582 refresh : function(){
38583 if(this.refreshDelegate){
38584 this.loaded = false;
38585 this.refreshDelegate();
38590 _setLoaded : function(){
38591 this.loaded = true;
38595 closeClick : function(e){
38598 this.fireEvent("beforeclose", this, o);
38599 if(o.cancel !== true){
38600 this.tabPanel.removeTab(this.id);
38604 * The text displayed in the tooltip for the close icon.
38607 closeText : "Close this tab"
38610 * This script refer to:
38611 * Title: International Telephone Input
38612 * Author: Jack O'Connor
38613 * Code version: v12.1.12
38614 * Availability: https://github.com/jackocnr/intl-tel-input.git
38617 Roo.bootstrap.PhoneInputData = function() {
38620 "Afghanistan (افغانستان)",
38625 "Albania (Shqipëri)",
38630 "Algeria (الجزائر)",
38655 "Antigua and Barbuda",
38665 "Armenia (Հայաստան)",
38681 "Austria (Österreich)",
38686 "Azerbaijan (Azərbaycan)",
38696 "Bahrain (البحرين)",
38701 "Bangladesh (বাংলাদেশ)",
38711 "Belarus (Беларусь)",
38716 "Belgium (België)",
38746 "Bosnia and Herzegovina (Босна и Херцеговина)",
38761 "British Indian Ocean Territory",
38766 "British Virgin Islands",
38776 "Bulgaria (България)",
38786 "Burundi (Uburundi)",
38791 "Cambodia (កម្ពុជា)",
38796 "Cameroon (Cameroun)",
38805 ["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"]
38808 "Cape Verde (Kabu Verdi)",
38813 "Caribbean Netherlands",
38824 "Central African Republic (République centrafricaine)",
38844 "Christmas Island",
38850 "Cocos (Keeling) Islands",
38861 "Comoros (جزر القمر)",
38866 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38871 "Congo (Republic) (Congo-Brazzaville)",
38891 "Croatia (Hrvatska)",
38912 "Czech Republic (Česká republika)",
38917 "Denmark (Danmark)",
38932 "Dominican Republic (República Dominicana)",
38936 ["809", "829", "849"]
38954 "Equatorial Guinea (Guinea Ecuatorial)",
38974 "Falkland Islands (Islas Malvinas)",
38979 "Faroe Islands (Føroyar)",
39000 "French Guiana (Guyane française)",
39005 "French Polynesia (Polynésie française)",
39020 "Georgia (საქართველო)",
39025 "Germany (Deutschland)",
39045 "Greenland (Kalaallit Nunaat)",
39082 "Guinea-Bissau (Guiné Bissau)",
39107 "Hungary (Magyarország)",
39112 "Iceland (Ísland)",
39132 "Iraq (العراق)",
39148 "Israel (ישראל)",
39175 "Jordan (الأردن)",
39180 "Kazakhstan (Казахстан)",
39201 "Kuwait (الكويت)",
39206 "Kyrgyzstan (Кыргызстан)",
39216 "Latvia (Latvija)",
39221 "Lebanon (لبنان)",
39236 "Libya (ليبيا)",
39246 "Lithuania (Lietuva)",
39261 "Macedonia (FYROM) (Македонија)",
39266 "Madagascar (Madagasikara)",
39296 "Marshall Islands",
39306 "Mauritania (موريتانيا)",
39311 "Mauritius (Moris)",
39332 "Moldova (Republica Moldova)",
39342 "Mongolia (Монгол)",
39347 "Montenegro (Crna Gora)",
39357 "Morocco (المغرب)",
39363 "Mozambique (Moçambique)",
39368 "Myanmar (Burma) (မြန်မာ)",
39373 "Namibia (Namibië)",
39388 "Netherlands (Nederland)",
39393 "New Caledonia (Nouvelle-Calédonie)",
39428 "North Korea (조선 민주주의 인민 공화국)",
39433 "Northern Mariana Islands",
39449 "Pakistan (پاکستان)",
39459 "Palestine (فلسطين)",
39469 "Papua New Guinea",
39511 "Réunion (La Réunion)",
39517 "Romania (România)",
39533 "Saint Barthélemy",
39544 "Saint Kitts and Nevis",
39554 "Saint Martin (Saint-Martin (partie française))",
39560 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39565 "Saint Vincent and the Grenadines",
39580 "São Tomé and Príncipe (São Tomé e Príncipe)",
39585 "Saudi Arabia (المملكة العربية السعودية)",
39590 "Senegal (Sénégal)",
39620 "Slovakia (Slovensko)",
39625 "Slovenia (Slovenija)",
39635 "Somalia (Soomaaliya)",
39645 "South Korea (대한민국)",
39650 "South Sudan (جنوب السودان)",
39660 "Sri Lanka (ශ්රී ලංකාව)",
39665 "Sudan (السودان)",
39675 "Svalbard and Jan Mayen",
39686 "Sweden (Sverige)",
39691 "Switzerland (Schweiz)",
39696 "Syria (سوريا)",
39741 "Trinidad and Tobago",
39746 "Tunisia (تونس)",
39751 "Turkey (Türkiye)",
39761 "Turks and Caicos Islands",
39771 "U.S. Virgin Islands",
39781 "Ukraine (Україна)",
39786 "United Arab Emirates (الإمارات العربية المتحدة)",
39808 "Uzbekistan (Oʻzbekiston)",
39818 "Vatican City (Città del Vaticano)",
39829 "Vietnam (Việt Nam)",
39834 "Wallis and Futuna (Wallis-et-Futuna)",
39839 "Western Sahara (الصحراء الغربية)",
39845 "Yemen (اليمن)",
39869 * This script refer to:
39870 * Title: International Telephone Input
39871 * Author: Jack O'Connor
39872 * Code version: v12.1.12
39873 * Availability: https://github.com/jackocnr/intl-tel-input.git
39877 * @class Roo.bootstrap.PhoneInput
39878 * @extends Roo.bootstrap.TriggerField
39879 * An input with International dial-code selection
39881 * @cfg {String} defaultDialCode default '+852'
39882 * @cfg {Array} preferedCountries default []
39885 * Create a new PhoneInput.
39886 * @param {Object} config Configuration options
39889 Roo.bootstrap.PhoneInput = function(config) {
39890 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39893 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39895 listWidth: undefined,
39897 selectedClass: 'active',
39899 invalidClass : "has-warning",
39901 validClass: 'has-success',
39903 allowed: '0123456789',
39908 * @cfg {String} defaultDialCode The default dial code when initializing the input
39910 defaultDialCode: '+852',
39913 * @cfg {Array} preferedCountries A list of iso2 in array (e.g. ['hk','us']). Those related countries will show at the top of the input's choices
39915 preferedCountries: false,
39917 getAutoCreate : function()
39919 var data = Roo.bootstrap.PhoneInputData();
39920 var align = this.labelAlign || this.parentLabelAlign();
39923 this.allCountries = [];
39924 this.dialCodeMapping = [];
39926 for (var i = 0; i < data.length; i++) {
39928 this.allCountries[i] = {
39932 priority: c[3] || 0,
39933 areaCodes: c[4] || null
39935 this.dialCodeMapping[c[2]] = {
39938 priority: c[3] || 0,
39939 areaCodes: c[4] || null
39951 // type: 'number', -- do not use number - we get the flaky up/down arrows.
39952 maxlength: this.max_length,
39953 cls : 'form-control tel-input',
39954 autocomplete: 'new-password'
39957 var hiddenInput = {
39960 cls: 'hidden-tel-input'
39964 hiddenInput.name = this.name;
39967 if (this.disabled) {
39968 input.disabled = true;
39971 var flag_container = {
39988 cls: this.hasFeedback ? 'has-feedback' : '',
39994 cls: 'dial-code-holder',
40001 cls: 'roo-select2-container input-group',
40008 if (this.fieldLabel.length) {
40011 tooltip: 'This field is required'
40017 cls: 'control-label',
40023 html: this.fieldLabel
40026 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40032 if(this.indicatorpos == 'right') {
40033 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40040 if(align == 'left') {
40048 if(this.labelWidth > 12){
40049 label.style = "width: " + this.labelWidth + 'px';
40051 if(this.labelWidth < 13 && this.labelmd == 0){
40052 this.labelmd = this.labelWidth;
40054 if(this.labellg > 0){
40055 label.cls += ' col-lg-' + this.labellg;
40056 input.cls += ' col-lg-' + (12 - this.labellg);
40058 if(this.labelmd > 0){
40059 label.cls += ' col-md-' + this.labelmd;
40060 container.cls += ' col-md-' + (12 - this.labelmd);
40062 if(this.labelsm > 0){
40063 label.cls += ' col-sm-' + this.labelsm;
40064 container.cls += ' col-sm-' + (12 - this.labelsm);
40066 if(this.labelxs > 0){
40067 label.cls += ' col-xs-' + this.labelxs;
40068 container.cls += ' col-xs-' + (12 - this.labelxs);
40078 var settings = this;
40080 ['xs','sm','md','lg'].map(function(size){
40081 if (settings[size]) {
40082 cfg.cls += ' col-' + size + '-' + settings[size];
40086 this.store = new Roo.data.Store({
40087 proxy : new Roo.data.MemoryProxy({}),
40088 reader : new Roo.data.JsonReader({
40099 'name' : 'dialCode',
40103 'name' : 'priority',
40107 'name' : 'areaCodes',
40114 if(!this.preferedCountries) {
40115 this.preferedCountries = [
40122 var p = this.preferedCountries.reverse();
40125 for (var i = 0; i < p.length; i++) {
40126 for (var j = 0; j < this.allCountries.length; j++) {
40127 if(this.allCountries[j].iso2 == p[i]) {
40128 var t = this.allCountries[j];
40129 this.allCountries.splice(j,1);
40130 this.allCountries.unshift(t);
40136 this.store.proxy.data = {
40138 data: this.allCountries
40144 initEvents : function()
40147 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40149 this.indicator = this.indicatorEl();
40150 this.flag = this.flagEl();
40151 this.dialCodeHolder = this.dialCodeHolderEl();
40153 this.trigger = this.el.select('div.flag-box',true).first();
40154 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40159 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40160 _this.list.setWidth(lw);
40163 this.list.on('mouseover', this.onViewOver, this);
40164 this.list.on('mousemove', this.onViewMove, this);
40165 this.inputEl().on("keyup", this.onKeyUp, this);
40166 this.inputEl().on("keypress", this.onKeyPress, this);
40168 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40170 this.view = new Roo.View(this.list, this.tpl, {
40171 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40174 this.view.on('click', this.onViewClick, this);
40175 this.setValue(this.defaultDialCode);
40178 onTriggerClick : function(e)
40180 Roo.log('trigger click');
40185 if(this.isExpanded()){
40187 this.hasFocus = false;
40189 this.store.load({});
40190 this.hasFocus = true;
40195 isExpanded : function()
40197 return this.list.isVisible();
40200 collapse : function()
40202 if(!this.isExpanded()){
40206 Roo.get(document).un('mousedown', this.collapseIf, this);
40207 Roo.get(document).un('mousewheel', this.collapseIf, this);
40208 this.fireEvent('collapse', this);
40212 expand : function()
40216 if(this.isExpanded() || !this.hasFocus){
40220 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40221 this.list.setWidth(lw);
40224 this.restrictHeight();
40226 Roo.get(document).on('mousedown', this.collapseIf, this);
40227 Roo.get(document).on('mousewheel', this.collapseIf, this);
40229 this.fireEvent('expand', this);
40232 restrictHeight : function()
40234 this.list.alignTo(this.inputEl(), this.listAlign);
40235 this.list.alignTo(this.inputEl(), this.listAlign);
40238 onViewOver : function(e, t)
40240 if(this.inKeyMode){
40243 var item = this.view.findItemFromChild(t);
40246 var index = this.view.indexOf(item);
40247 this.select(index, false);
40252 onViewClick : function(view, doFocus, el, e)
40254 var index = this.view.getSelectedIndexes()[0];
40256 var r = this.store.getAt(index);
40259 this.onSelect(r, index);
40261 if(doFocus !== false && !this.blockFocus){
40262 this.inputEl().focus();
40266 onViewMove : function(e, t)
40268 this.inKeyMode = false;
40271 select : function(index, scrollIntoView)
40273 this.selectedIndex = index;
40274 this.view.select(index);
40275 if(scrollIntoView !== false){
40276 var el = this.view.getNode(index);
40278 this.list.scrollChildIntoView(el, false);
40283 createList : function()
40285 this.list = Roo.get(document.body).createChild({
40287 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40288 style: 'display:none'
40291 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40294 collapseIf : function(e)
40296 var in_combo = e.within(this.el);
40297 var in_list = e.within(this.list);
40298 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40300 if (in_combo || in_list || is_list) {
40306 onSelect : function(record, index)
40308 if(this.fireEvent('beforeselect', this, record, index) !== false){
40310 this.setFlagClass(record.data.iso2);
40311 this.setDialCode(record.data.dialCode);
40312 this.hasFocus = false;
40314 this.fireEvent('select', this, record, index);
40318 flagEl : function()
40320 var flag = this.el.select('div.flag',true).first();
40327 dialCodeHolderEl : function()
40329 var d = this.el.select('input.dial-code-holder',true).first();
40336 setDialCode : function(v)
40338 this.dialCodeHolder.dom.value = '+'+v;
40341 setFlagClass : function(n)
40343 this.flag.dom.className = 'flag '+n;
40346 getValue : function()
40348 var v = this.inputEl().getValue();
40349 if(this.dialCodeHolder) {
40350 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40355 setValue : function(v)
40357 var d = this.getDialCode(v);
40359 //invalid dial code
40360 if(v.length == 0 || !d || d.length == 0) {
40362 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40363 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40369 this.setFlagClass(this.dialCodeMapping[d].iso2);
40370 this.setDialCode(d);
40371 this.inputEl().dom.value = v.replace('+'+d,'');
40372 this.hiddenEl().dom.value = this.getValue();
40377 getDialCode : function(v)
40381 if (v.length == 0) {
40382 return this.dialCodeHolder.dom.value;
40386 if (v.charAt(0) != "+") {
40389 var numericChars = "";
40390 for (var i = 1; i < v.length; i++) {
40391 var c = v.charAt(i);
40394 if (this.dialCodeMapping[numericChars]) {
40395 dialCode = v.substr(1, i);
40397 if (numericChars.length == 4) {
40407 this.setValue(this.defaultDialCode);
40411 hiddenEl : function()
40413 return this.el.select('input.hidden-tel-input',true).first();
40416 // after setting val
40417 onKeyUp : function(e){
40418 this.setValue(this.getValue());
40421 onKeyPress : function(e){
40422 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40429 * @class Roo.bootstrap.MoneyField
40430 * @extends Roo.bootstrap.ComboBox
40431 * Bootstrap MoneyField class
40434 * Create a new MoneyField.
40435 * @param {Object} config Configuration options
40438 Roo.bootstrap.MoneyField = function(config) {
40440 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40444 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40447 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40449 allowDecimals : true,
40451 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40453 decimalSeparator : ".",
40455 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40457 decimalPrecision : 0,
40459 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40461 allowNegative : true,
40463 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40467 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40469 minValue : Number.NEGATIVE_INFINITY,
40471 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40473 maxValue : Number.MAX_VALUE,
40475 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40477 minText : "The minimum value for this field is {0}",
40479 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40481 maxText : "The maximum value for this field is {0}",
40483 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
40484 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40486 nanText : "{0} is not a valid number",
40488 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40492 * @cfg {String} defaults currency of the MoneyField
40493 * value should be in lkey
40495 defaultCurrency : false,
40497 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40499 thousandsDelimiter : false,
40501 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40512 getAutoCreate : function()
40514 var align = this.labelAlign || this.parentLabelAlign();
40526 cls : 'form-control roo-money-amount-input',
40527 autocomplete: 'new-password'
40530 var hiddenInput = {
40534 cls: 'hidden-number-input'
40537 if(this.max_length) {
40538 input.maxlength = this.max_length;
40542 hiddenInput.name = this.name;
40545 if (this.disabled) {
40546 input.disabled = true;
40549 var clg = 12 - this.inputlg;
40550 var cmd = 12 - this.inputmd;
40551 var csm = 12 - this.inputsm;
40552 var cxs = 12 - this.inputxs;
40556 cls : 'row roo-money-field',
40560 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40564 cls: 'roo-select2-container input-group',
40568 cls : 'form-control roo-money-currency-input',
40569 autocomplete: 'new-password',
40571 name : this.currencyName
40575 cls : 'input-group-addon',
40589 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40593 cls: this.hasFeedback ? 'has-feedback' : '',
40604 if (this.fieldLabel.length) {
40607 tooltip: 'This field is required'
40613 cls: 'control-label',
40619 html: this.fieldLabel
40622 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40628 if(this.indicatorpos == 'right') {
40629 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40636 if(align == 'left') {
40644 if(this.labelWidth > 12){
40645 label.style = "width: " + this.labelWidth + 'px';
40647 if(this.labelWidth < 13 && this.labelmd == 0){
40648 this.labelmd = this.labelWidth;
40650 if(this.labellg > 0){
40651 label.cls += ' col-lg-' + this.labellg;
40652 input.cls += ' col-lg-' + (12 - this.labellg);
40654 if(this.labelmd > 0){
40655 label.cls += ' col-md-' + this.labelmd;
40656 container.cls += ' col-md-' + (12 - this.labelmd);
40658 if(this.labelsm > 0){
40659 label.cls += ' col-sm-' + this.labelsm;
40660 container.cls += ' col-sm-' + (12 - this.labelsm);
40662 if(this.labelxs > 0){
40663 label.cls += ' col-xs-' + this.labelxs;
40664 container.cls += ' col-xs-' + (12 - this.labelxs);
40675 var settings = this;
40677 ['xs','sm','md','lg'].map(function(size){
40678 if (settings[size]) {
40679 cfg.cls += ' col-' + size + '-' + settings[size];
40686 initEvents : function()
40688 this.indicator = this.indicatorEl();
40690 this.initCurrencyEvent();
40692 this.initNumberEvent();
40695 initCurrencyEvent : function()
40698 throw "can not find store for combo";
40701 this.store = Roo.factory(this.store, Roo.data);
40702 this.store.parent = this;
40706 this.triggerEl = this.el.select('.input-group-addon', true).first();
40708 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40713 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40714 _this.list.setWidth(lw);
40717 this.list.on('mouseover', this.onViewOver, this);
40718 this.list.on('mousemove', this.onViewMove, this);
40719 this.list.on('scroll', this.onViewScroll, this);
40722 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40725 this.view = new Roo.View(this.list, this.tpl, {
40726 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40729 this.view.on('click', this.onViewClick, this);
40731 this.store.on('beforeload', this.onBeforeLoad, this);
40732 this.store.on('load', this.onLoad, this);
40733 this.store.on('loadexception', this.onLoadException, this);
40735 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40736 "up" : function(e){
40737 this.inKeyMode = true;
40741 "down" : function(e){
40742 if(!this.isExpanded()){
40743 this.onTriggerClick();
40745 this.inKeyMode = true;
40750 "enter" : function(e){
40753 if(this.fireEvent("specialkey", this, e)){
40754 this.onViewClick(false);
40760 "esc" : function(e){
40764 "tab" : function(e){
40767 if(this.fireEvent("specialkey", this, e)){
40768 this.onViewClick(false);
40776 doRelay : function(foo, bar, hname){
40777 if(hname == 'down' || this.scope.isExpanded()){
40778 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40786 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40790 initNumberEvent : function(e)
40792 this.inputEl().on("keydown" , this.fireKey, this);
40793 this.inputEl().on("focus", this.onFocus, this);
40794 this.inputEl().on("blur", this.onBlur, this);
40796 this.inputEl().relayEvent('keyup', this);
40798 if(this.indicator){
40799 this.indicator.addClass('invisible');
40802 this.originalValue = this.getValue();
40804 if(this.validationEvent == 'keyup'){
40805 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40806 this.inputEl().on('keyup', this.filterValidation, this);
40808 else if(this.validationEvent !== false){
40809 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40812 if(this.selectOnFocus){
40813 this.on("focus", this.preFocus, this);
40816 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40817 this.inputEl().on("keypress", this.filterKeys, this);
40819 this.inputEl().relayEvent('keypress', this);
40822 var allowed = "0123456789";
40824 if(this.allowDecimals){
40825 allowed += this.decimalSeparator;
40828 if(this.allowNegative){
40832 if(this.thousandsDelimiter) {
40836 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40838 var keyPress = function(e){
40840 var k = e.getKey();
40842 var c = e.getCharCode();
40845 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40846 allowed.indexOf(String.fromCharCode(c)) === -1
40852 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40856 if(allowed.indexOf(String.fromCharCode(c)) === -1){
40861 this.inputEl().on("keypress", keyPress, this);
40865 onTriggerClick : function(e)
40872 this.loadNext = false;
40874 if(this.isExpanded()){
40879 this.hasFocus = true;
40881 if(this.triggerAction == 'all') {
40882 this.doQuery(this.allQuery, true);
40886 this.doQuery(this.getRawValue());
40889 getCurrency : function()
40891 var v = this.currencyEl().getValue();
40896 restrictHeight : function()
40898 this.list.alignTo(this.currencyEl(), this.listAlign);
40899 this.list.alignTo(this.currencyEl(), this.listAlign);
40902 onViewClick : function(view, doFocus, el, e)
40904 var index = this.view.getSelectedIndexes()[0];
40906 var r = this.store.getAt(index);
40909 this.onSelect(r, index);
40913 onSelect : function(record, index){
40915 if(this.fireEvent('beforeselect', this, record, index) !== false){
40917 this.setFromCurrencyData(index > -1 ? record.data : false);
40921 this.fireEvent('select', this, record, index);
40925 setFromCurrencyData : function(o)
40929 this.lastCurrency = o;
40931 if (this.currencyField) {
40932 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40934 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
40937 this.lastSelectionText = currency;
40939 //setting default currency
40940 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40941 this.setCurrency(this.defaultCurrency);
40945 this.setCurrency(currency);
40948 setFromData : function(o)
40952 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40954 this.setFromCurrencyData(c);
40959 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40961 Roo.log('no value set for '+ (this.name ? this.name : this.id));
40964 this.setValue(value);
40968 setCurrency : function(v)
40970 this.currencyValue = v;
40973 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40978 setValue : function(v)
40980 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40986 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40988 this.inputEl().dom.value = (v == '') ? '' :
40989 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
40991 if(!this.allowZero && v === '0') {
40992 this.hiddenEl().dom.value = '';
40993 this.inputEl().dom.value = '';
41000 getRawValue : function()
41002 var v = this.inputEl().getValue();
41007 getValue : function()
41009 return this.fixPrecision(this.parseValue(this.getRawValue()));
41012 parseValue : function(value)
41014 if(this.thousandsDelimiter) {
41016 r = new RegExp(",", "g");
41017 value = value.replace(r, "");
41020 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41021 return isNaN(value) ? '' : value;
41025 fixPrecision : function(value)
41027 if(this.thousandsDelimiter) {
41029 r = new RegExp(",", "g");
41030 value = value.replace(r, "");
41033 var nan = isNaN(value);
41035 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41036 return nan ? '' : value;
41038 return parseFloat(value).toFixed(this.decimalPrecision);
41041 decimalPrecisionFcn : function(v)
41043 return Math.floor(v);
41046 validateValue : function(value)
41048 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41052 var num = this.parseValue(value);
41055 this.markInvalid(String.format(this.nanText, value));
41059 if(num < this.minValue){
41060 this.markInvalid(String.format(this.minText, this.minValue));
41064 if(num > this.maxValue){
41065 this.markInvalid(String.format(this.maxText, this.maxValue));
41072 validate : function()
41074 if(this.disabled || this.allowBlank){
41079 var currency = this.getCurrency();
41081 if(this.validateValue(this.getRawValue()) && currency.length){
41086 this.markInvalid();
41090 getName: function()
41095 beforeBlur : function()
41101 var v = this.parseValue(this.getRawValue());
41108 onBlur : function()
41112 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41113 //this.el.removeClass(this.focusClass);
41116 this.hasFocus = false;
41118 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41122 var v = this.getValue();
41124 if(String(v) !== String(this.startValue)){
41125 this.fireEvent('change', this, v, this.startValue);
41128 this.fireEvent("blur", this);
41131 inputEl : function()
41133 return this.el.select('.roo-money-amount-input', true).first();
41136 currencyEl : function()
41138 return this.el.select('.roo-money-currency-input', true).first();
41141 hiddenEl : function()
41143 return this.el.select('input.hidden-number-input',true).first();