4 * base class for bootstrap elements.
8 Roo.bootstrap = Roo.bootstrap || {};
10 * @class Roo.bootstrap.Component
11 * @extends Roo.Component
12 * Bootstrap Component base class
13 * @cfg {String} cls css class
14 * @cfg {String} style any extra css
15 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
17 * @cfg {string} dataId cutomer id
18 * @cfg {string} name Specifies name attribute
19 * @cfg {string} tooltip Text for the tooltip
20 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
23 * Do not use directly - it does not do anything..
24 * @param {Object} config The config object
29 Roo.bootstrap.Component = function(config){
30 Roo.bootstrap.Component.superclass.constructor.call(this, config);
34 * @event childrenrendered
35 * Fires when the children have been rendered..
36 * @param {Roo.bootstrap.Component} this
38 "childrenrendered" : true
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
50 allowDomMove : false, // to stop relocations in parent onRender...
60 * Initialize Events for the element
62 initEvents : function() { },
68 can_build_overlaid : true,
70 container_method : false,
77 // returns the parent component..
78 return Roo.ComponentMgr.get(this.parentId)
84 onRender : function(ct, position)
86 // Roo.log("Call onRender: " + this.xtype);
88 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
91 if (this.el.attr('xtype')) {
92 this.el.attr('xtypex', this.el.attr('xtype'));
93 this.el.dom.removeAttribute('xtype');
103 var cfg = Roo.apply({}, this.getAutoCreate());
105 cfg.id = this.id || Roo.id();
107 // fill in the extra attributes
108 if (this.xattr && typeof(this.xattr) =='object') {
109 for (var i in this.xattr) {
110 cfg[i] = this.xattr[i];
115 cfg.dataId = this.dataId;
119 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
122 if (this.style) { // fixme needs to support more complex style data.
123 cfg.style = this.style;
127 cfg.name = this.name;
130 this.el = ct.createChild(cfg, position);
133 this.tooltipEl().attr('tooltip', this.tooltip);
136 if(this.tabIndex !== undefined){
137 this.el.dom.setAttribute('tabIndex', this.tabIndex);
144 * Fetch the element to add children to
145 * @return {Roo.Element} defaults to this.el
147 getChildContainer : function()
152 * Fetch the element to display the tooltip on.
153 * @return {Roo.Element} defaults to this.el
155 tooltipEl : function()
160 addxtype : function(tree,cntr)
164 cn = Roo.factory(tree);
165 //Roo.log(['addxtype', cn]);
167 cn.parentType = this.xtype; //??
168 cn.parentId = this.id;
170 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
171 if (typeof(cn.container_method) == 'string') {
172 cntr = cn.container_method;
176 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
178 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
180 var build_from_html = Roo.XComponent.build_from_html;
182 var is_body = (tree.xtype == 'Body') ;
184 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
186 var self_cntr_el = Roo.get(this[cntr](false));
188 // do not try and build conditional elements
189 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
193 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
194 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
195 return this.addxtypeChild(tree,cntr, is_body);
198 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
201 return this.addxtypeChild(Roo.apply({}, tree),cntr);
204 Roo.log('skipping render');
210 if (!build_from_html) {
214 // this i think handles overlaying multiple children of the same type
215 // with the sam eelement.. - which might be buggy..
217 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
223 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
227 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
234 addxtypeChild : function (tree, cntr, is_body)
236 Roo.debug && Roo.log('addxtypeChild:' + cntr);
238 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
241 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
242 (typeof(tree['flexy:foreach']) != 'undefined');
246 skip_children = false;
247 // render the element if it's not BODY.
250 Roo.log(this[cntr](true));
251 // if parent was disabled, then do not try and create the children..
252 if(this[cntr](true) === false || this[cntr](true) === null){
257 cn = Roo.factory(tree);
259 cn.parentType = this.xtype; //??
260 cn.parentId = this.id;
262 var build_from_html = Roo.XComponent.build_from_html;
265 // does the container contain child eleemnts with 'xtype' attributes.
266 // that match this xtype..
267 // note - when we render we create these as well..
268 // so we should check to see if body has xtype set.
269 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
271 var self_cntr_el = Roo.get(this[cntr](false));
272 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
274 //Roo.log(Roo.XComponent.build_from_html);
275 //Roo.log("got echild:");
278 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
279 // and are not displayed -this causes this to use up the wrong element when matching.
280 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
283 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
284 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
290 //echild.dom.removeAttribute('xtype');
292 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
293 Roo.debug && Roo.log(self_cntr_el);
294 Roo.debug && Roo.log(echild);
295 Roo.debug && Roo.log(cn);
301 // if object has flexy:if - then it may or may not be rendered.
302 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
303 // skip a flexy if element.
304 Roo.debug && Roo.log('skipping render');
305 Roo.debug && Roo.log(tree);
307 Roo.debug && Roo.log('skipping all children');
308 skip_children = true;
313 // actually if flexy:foreach is found, we really want to create
314 // multiple copies here...
316 //Roo.log(this[cntr]());
317 // some elements do not have render methods.. like the layouts...
319 if(this[cntr](true) === false){
324 cn.render && cn.render(this[cntr](true));
327 // then add the element..
334 if (typeof (tree.menu) != 'undefined') {
335 tree.menu.parentType = cn.xtype;
336 tree.menu.triggerEl = cn.el;
337 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
341 if (!tree.items || !tree.items.length) {
343 //Roo.log(["no children", this]);
348 var items = tree.items;
351 //Roo.log(items.length);
353 if (!skip_children) {
354 for(var i =0;i < items.length;i++) {
355 // Roo.log(['add child', items[i]]);
356 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
362 //Roo.log("fire childrenrendered");
364 cn.fireEvent('childrenrendered', this);
369 * Show a component - removes 'hidden' class
374 this.el.removeClass('hidden');
378 * Hide a component - adds 'hidden' class
382 if (this.el && !this.el.hasClass('hidden')) {
383 this.el.addClass('hidden');
396 * @class Roo.bootstrap.Body
397 * @extends Roo.bootstrap.Component
398 * Bootstrap Body class
402 * @param {Object} config The config object
405 Roo.bootstrap.Body = function(config){
407 config = config || {};
409 Roo.bootstrap.Body.superclass.constructor.call(this, config);
410 this.el = Roo.get(config.el ? config.el : document.body );
411 if (this.cls && this.cls.length) {
412 Roo.get(document.body).addClass(this.cls);
416 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
418 is_body : true,// just to make sure it's constructed?
423 onRender : function(ct, position)
425 /* Roo.log("Roo.bootstrap.Body - onRender");
426 if (this.cls && this.cls.length) {
427 Roo.get(document.body).addClass(this.cls);
446 * @class Roo.bootstrap.ButtonGroup
447 * @extends Roo.bootstrap.Component
448 * Bootstrap ButtonGroup class
449 * @cfg {String} size lg | sm | xs (default empty normal)
450 * @cfg {String} align vertical | justified (default none)
451 * @cfg {String} direction up | down (default down)
452 * @cfg {Boolean} toolbar false | true
453 * @cfg {Boolean} btn true | false
458 * @param {Object} config The config object
461 Roo.bootstrap.ButtonGroup = function(config){
462 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
465 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
473 getAutoCreate : function(){
479 cfg.html = this.html || cfg.html;
490 if (['vertical','justified'].indexOf(this.align)!==-1) {
491 cfg.cls = 'btn-group-' + this.align;
493 if (this.align == 'justified') {
494 console.log(this.items);
498 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
499 cfg.cls += ' btn-group-' + this.size;
502 if (this.direction == 'up') {
503 cfg.cls += ' dropup' ;
519 * @class Roo.bootstrap.Button
520 * @extends Roo.bootstrap.Component
521 * Bootstrap Button class
522 * @cfg {String} html The button content
523 * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default
524 * @cfg {String} size ( lg | sm | xs)
525 * @cfg {String} tag ( a | input | submit)
526 * @cfg {String} href empty or href
527 * @cfg {Boolean} disabled default false;
528 * @cfg {Boolean} isClose default false;
529 * @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)
530 * @cfg {String} badge text for badge
531 * @cfg {String} theme default
532 * @cfg {Boolean} inverse
533 * @cfg {Boolean} toggle
534 * @cfg {String} ontext text for on toggle state
535 * @cfg {String} offtext text for off toggle state
536 * @cfg {Boolean} defaulton
537 * @cfg {Boolean} preventDefault default true
538 * @cfg {Boolean} removeClass remove the standard class..
539 * @cfg {String} target target for a href. (_self|_blank|_parent|_top| other)
542 * Create a new button
543 * @param {Object} config The config object
547 Roo.bootstrap.Button = function(config){
548 Roo.bootstrap.Button.superclass.constructor.call(this, config);
549 this.weightClass = ["btn-default",
561 * When a butotn is pressed
562 * @param {Roo.bootstrap.Button} this
563 * @param {Roo.EventObject} e
568 * After the button has been toggles
569 * @param {Roo.EventObject} e
570 * @param {boolean} pressed (also available as button.pressed)
576 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
594 preventDefault: true,
603 getAutoCreate : function(){
611 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
612 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
617 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
619 if (this.toggle == true) {
622 cls: 'slider-frame roo-button',
627 'data-off-text':'OFF',
628 cls: 'slider-button',
634 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
635 cfg.cls += ' '+this.weight;
644 cfg["aria-hidden"] = true;
646 cfg.html = "×";
652 if (this.theme==='default') {
653 cfg.cls = 'btn roo-button';
655 //if (this.parentType != 'Navbar') {
656 this.weight = this.weight.length ? this.weight : 'default';
658 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
660 cfg.cls += ' btn-' + this.weight;
662 } else if (this.theme==='glow') {
665 cfg.cls = 'btn-glow roo-button';
667 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
669 cfg.cls += ' ' + this.weight;
675 this.cls += ' inverse';
680 cfg.cls += ' active';
684 cfg.disabled = 'disabled';
688 Roo.log('changing to ul' );
690 this.glyphicon = 'caret';
693 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
695 //gsRoo.log(this.parentType);
696 if (this.parentType === 'Navbar' && !this.parent().bar) {
697 Roo.log('changing to li?');
706 href : this.href || '#'
709 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
710 cfg.cls += ' dropdown';
717 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
719 if (this.glyphicon) {
720 cfg.html = ' ' + cfg.html;
725 cls: 'glyphicon glyphicon-' + this.glyphicon
735 // cfg.cls='btn roo-button';
739 var value = cfg.html;
744 cls: 'glyphicon glyphicon-' + this.glyphicon,
763 cfg.cls += ' dropdown';
764 cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
767 if (cfg.tag !== 'a' && this.href !== '') {
768 throw "Tag must be a to set href.";
769 } else if (this.href.length > 0) {
770 cfg.href = this.href;
773 if(this.removeClass){
778 cfg.target = this.target;
783 initEvents: function() {
784 // Roo.log('init events?');
785 // Roo.log(this.el.dom);
788 if (typeof (this.menu) != 'undefined') {
789 this.menu.parentType = this.xtype;
790 this.menu.triggerEl = this.el;
791 this.addxtype(Roo.apply({}, this.menu));
795 if (this.el.hasClass('roo-button')) {
796 this.el.on('click', this.onClick, this);
798 this.el.select('.roo-button').on('click', this.onClick, this);
801 if(this.removeClass){
802 this.el.on('click', this.onClick, this);
805 this.el.enableDisplayMode();
808 onClick : function(e)
815 Roo.log('button on click ');
816 if(this.preventDefault){
819 if (this.pressed === true || this.pressed === false) {
820 this.pressed = !this.pressed;
821 this.el[this.pressed ? 'addClass' : 'removeClass']('active');
822 this.fireEvent('toggle', this, e, this.pressed);
826 this.fireEvent('click', this, e);
830 * Enables this button
834 this.disabled = false;
835 this.el.removeClass('disabled');
839 * Disable this button
843 this.disabled = true;
844 this.el.addClass('disabled');
847 * sets the active state on/off,
848 * @param {Boolean} state (optional) Force a particular state
850 setActive : function(v) {
852 this.el[v ? 'addClass' : 'removeClass']('active');
855 * toggles the current active state
857 toggleActive : function()
859 var active = this.el.hasClass('active');
860 this.setActive(!active);
864 setText : function(str)
866 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
870 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
881 setWeight : function(str)
883 this.el.removeClass(this.weightClass);
884 this.el.addClass('btn-' + str);
898 * @class Roo.bootstrap.Column
899 * @extends Roo.bootstrap.Component
900 * Bootstrap Column class
901 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
902 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
903 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
904 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
905 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
906 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
907 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
908 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
911 * @cfg {Boolean} hidden (true|false) hide the element
912 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
913 * @cfg {String} fa (ban|check|...) font awesome icon
914 * @cfg {Number} fasize (1|2|....) font awsome size
916 * @cfg {String} icon (info-sign|check|...) glyphicon name
918 * @cfg {String} html content of column.
921 * Create a new Column
922 * @param {Object} config The config object
925 Roo.bootstrap.Column = function(config){
926 Roo.bootstrap.Column.superclass.constructor.call(this, config);
929 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
947 getAutoCreate : function(){
948 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
956 ['xs','sm','md','lg'].map(function(size){
957 //Roo.log( size + ':' + settings[size]);
959 if (settings[size+'off'] !== false) {
960 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
963 if (settings[size] === false) {
967 if (!settings[size]) { // 0 = hidden
968 cfg.cls += ' hidden-' + size;
971 cfg.cls += ' col-' + size + '-' + settings[size];
976 cfg.cls += ' hidden';
979 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
980 cfg.cls +=' alert alert-' + this.alert;
984 if (this.html.length) {
985 cfg.html = this.html;
989 if (this.fasize > 1) {
990 fasize = ' fa-' + this.fasize + 'x';
992 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
997 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1016 * @class Roo.bootstrap.Container
1017 * @extends Roo.bootstrap.Component
1018 * Bootstrap Container class
1019 * @cfg {Boolean} jumbotron is it a jumbotron element
1020 * @cfg {String} html content of element
1021 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1022 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1023 * @cfg {String} header content of header (for panel)
1024 * @cfg {String} footer content of footer (for panel)
1025 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1026 * @cfg {String} tag (header|aside|section) type of HTML tag.
1027 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1028 * @cfg {String} fa font awesome icon
1029 * @cfg {String} icon (info-sign|check|...) glyphicon name
1030 * @cfg {Boolean} hidden (true|false) hide the element
1031 * @cfg {Boolean} expandable (true|false) default false
1032 * @cfg {Boolean} expanded (true|false) default true
1033 * @cfg {String} rheader contet on the right of header
1034 * @cfg {Boolean} clickable (true|false) default false
1038 * Create a new Container
1039 * @param {Object} config The config object
1042 Roo.bootstrap.Container = function(config){
1043 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1049 * After the panel has been expand
1051 * @param {Roo.bootstrap.Container} this
1056 * After the panel has been collapsed
1058 * @param {Roo.bootstrap.Container} this
1063 * When a element is chick
1064 * @param {Roo.bootstrap.Container} this
1065 * @param {Roo.EventObject} e
1071 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1089 getChildContainer : function() {
1095 if (this.panel.length) {
1096 return this.el.select('.panel-body',true).first();
1103 getAutoCreate : function(){
1106 tag : this.tag || 'div',
1110 if (this.jumbotron) {
1111 cfg.cls = 'jumbotron';
1116 // - this is applied by the parent..
1118 // cfg.cls = this.cls + '';
1121 if (this.sticky.length) {
1123 var bd = Roo.get(document.body);
1124 if (!bd.hasClass('bootstrap-sticky')) {
1125 bd.addClass('bootstrap-sticky');
1126 Roo.select('html',true).setStyle('height', '100%');
1129 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1133 if (this.well.length) {
1134 switch (this.well) {
1137 cfg.cls +=' well well-' +this.well;
1146 cfg.cls += ' hidden';
1150 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1151 cfg.cls +=' alert alert-' + this.alert;
1156 if (this.panel.length) {
1157 cfg.cls += ' panel panel-' + this.panel;
1159 if (this.header.length) {
1163 if(this.expandable){
1165 cfg.cls = cfg.cls + ' expandable';
1169 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1177 cls : 'panel-title',
1178 html : (this.expandable ? ' ' : '') + this.header
1182 cls: 'panel-header-right',
1188 cls : 'panel-heading',
1189 style : this.expandable ? 'cursor: pointer' : '',
1197 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1202 if (this.footer.length) {
1204 cls : 'panel-footer',
1213 body.html = this.html || cfg.html;
1214 // prefix with the icons..
1216 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1219 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1224 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1225 cfg.cls = 'container';
1231 initEvents: function()
1233 if(this.expandable){
1234 var headerEl = this.headerEl();
1237 headerEl.on('click', this.onToggleClick, this);
1242 this.el.on('click', this.onClick, this);
1247 onToggleClick : function()
1249 var headerEl = this.headerEl();
1265 if(this.fireEvent('expand', this)) {
1267 this.expanded = true;
1269 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1271 this.el.select('.panel-body',true).first().removeClass('hide');
1273 var toggleEl = this.toggleEl();
1279 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1284 collapse : function()
1286 if(this.fireEvent('collapse', this)) {
1288 this.expanded = false;
1290 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1291 this.el.select('.panel-body',true).first().addClass('hide');
1293 var toggleEl = this.toggleEl();
1299 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1303 toggleEl : function()
1305 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1309 return this.el.select('.panel-heading .fa',true).first();
1312 headerEl : function()
1314 if(!this.el || !this.panel.length || !this.header.length){
1318 return this.el.select('.panel-heading',true).first()
1323 if(!this.el || !this.panel.length){
1327 return this.el.select('.panel-body',true).first()
1330 titleEl : function()
1332 if(!this.el || !this.panel.length || !this.header.length){
1336 return this.el.select('.panel-title',true).first();
1339 setTitle : function(v)
1341 var titleEl = this.titleEl();
1347 titleEl.dom.innerHTML = v;
1350 getTitle : function()
1353 var titleEl = this.titleEl();
1359 return titleEl.dom.innerHTML;
1362 setRightTitle : function(v)
1364 var t = this.el.select('.panel-header-right',true).first();
1370 t.dom.innerHTML = v;
1373 onClick : function(e)
1377 this.fireEvent('click', this, e);
1391 * @class Roo.bootstrap.Img
1392 * @extends Roo.bootstrap.Component
1393 * Bootstrap Img class
1394 * @cfg {Boolean} imgResponsive false | true
1395 * @cfg {String} border rounded | circle | thumbnail
1396 * @cfg {String} src image source
1397 * @cfg {String} alt image alternative text
1398 * @cfg {String} href a tag href
1399 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1400 * @cfg {String} xsUrl xs image source
1401 * @cfg {String} smUrl sm image source
1402 * @cfg {String} mdUrl md image source
1403 * @cfg {String} lgUrl lg image source
1406 * Create a new Input
1407 * @param {Object} config The config object
1410 Roo.bootstrap.Img = function(config){
1411 Roo.bootstrap.Img.superclass.constructor.call(this, config);
1417 * The img click event for the img.
1418 * @param {Roo.EventObject} e
1424 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1426 imgResponsive: true,
1436 getAutoCreate : function()
1438 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1439 return this.createSingleImg();
1444 cls: 'roo-image-responsive-group',
1449 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1451 if(!_this[size + 'Url']){
1457 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1458 html: _this.html || cfg.html,
1459 src: _this[size + 'Url']
1462 img.cls += ' roo-image-responsive-' + size;
1464 var s = ['xs', 'sm', 'md', 'lg'];
1466 s.splice(s.indexOf(size), 1);
1468 Roo.each(s, function(ss){
1469 img.cls += ' hidden-' + ss;
1472 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1473 cfg.cls += ' img-' + _this.border;
1477 cfg.alt = _this.alt;
1490 a.target = _this.target;
1494 cfg.cn.push((_this.href) ? a : img);
1501 createSingleImg : function()
1505 cls: (this.imgResponsive) ? 'img-responsive' : '',
1507 src : 'about:blank' // just incase src get's set to undefined?!?
1510 cfg.html = this.html || cfg.html;
1512 cfg.src = this.src || cfg.src;
1514 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1515 cfg.cls += ' img-' + this.border;
1532 a.target = this.target;
1537 return (this.href) ? a : cfg;
1540 initEvents: function()
1543 this.el.on('click', this.onClick, this);
1548 onClick : function(e)
1550 Roo.log('img onclick');
1551 this.fireEvent('click', this, e);
1554 * Sets the url of the image - used to update it
1555 * @param {String} url the url of the image
1558 setSrc : function(url)
1562 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1563 this.el.dom.src = url;
1567 this.el.select('img', true).first().dom.src = url;
1583 * @class Roo.bootstrap.Link
1584 * @extends Roo.bootstrap.Component
1585 * Bootstrap Link Class
1586 * @cfg {String} alt image alternative text
1587 * @cfg {String} href a tag href
1588 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1589 * @cfg {String} html the content of the link.
1590 * @cfg {String} anchor name for the anchor link
1591 * @cfg {String} fa - favicon
1593 * @cfg {Boolean} preventDefault (true | false) default false
1597 * Create a new Input
1598 * @param {Object} config The config object
1601 Roo.bootstrap.Link = function(config){
1602 Roo.bootstrap.Link.superclass.constructor.call(this, config);
1608 * The img click event for the img.
1609 * @param {Roo.EventObject} e
1615 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
1619 preventDefault: false,
1625 getAutoCreate : function()
1627 var html = this.html || '';
1629 if (this.fa !== false) {
1630 html = '<i class="fa fa-' + this.fa + '"></i>';
1635 // anchor's do not require html/href...
1636 if (this.anchor === false) {
1638 cfg.href = this.href || '#';
1640 cfg.name = this.anchor;
1641 if (this.html !== false || this.fa !== false) {
1644 if (this.href !== false) {
1645 cfg.href = this.href;
1649 if(this.alt !== false){
1654 if(this.target !== false) {
1655 cfg.target = this.target;
1661 initEvents: function() {
1663 if(!this.href || this.preventDefault){
1664 this.el.on('click', this.onClick, this);
1668 onClick : function(e)
1670 if(this.preventDefault){
1673 //Roo.log('img onclick');
1674 this.fireEvent('click', this, e);
1687 * @class Roo.bootstrap.Header
1688 * @extends Roo.bootstrap.Component
1689 * Bootstrap Header class
1690 * @cfg {String} html content of header
1691 * @cfg {Number} level (1|2|3|4|5|6) default 1
1694 * Create a new Header
1695 * @param {Object} config The config object
1699 Roo.bootstrap.Header = function(config){
1700 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1703 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1711 getAutoCreate : function(){
1716 tag: 'h' + (1 *this.level),
1717 html: this.html || ''
1729 * Ext JS Library 1.1.1
1730 * Copyright(c) 2006-2007, Ext JS, LLC.
1732 * Originally Released Under LGPL - original licence link has changed is not relivant.
1735 * <script type="text/javascript">
1739 * @class Roo.bootstrap.MenuMgr
1740 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1743 Roo.bootstrap.MenuMgr = function(){
1744 var menus, active, groups = {}, attached = false, lastShow = new Date();
1746 // private - called when first menu is created
1749 active = new Roo.util.MixedCollection();
1750 Roo.get(document).addKeyListener(27, function(){
1751 if(active.length > 0){
1759 if(active && active.length > 0){
1760 var c = active.clone();
1770 if(active.length < 1){
1771 Roo.get(document).un("mouseup", onMouseDown);
1779 var last = active.last();
1780 lastShow = new Date();
1783 Roo.get(document).on("mouseup", onMouseDown);
1788 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1789 m.parentMenu.activeChild = m;
1790 }else if(last && last.isVisible()){
1791 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1796 function onBeforeHide(m){
1798 m.activeChild.hide();
1800 if(m.autoHideTimer){
1801 clearTimeout(m.autoHideTimer);
1802 delete m.autoHideTimer;
1807 function onBeforeShow(m){
1808 var pm = m.parentMenu;
1809 if(!pm && !m.allowOtherMenus){
1811 }else if(pm && pm.activeChild && active != m){
1812 pm.activeChild.hide();
1816 // private this should really trigger on mouseup..
1817 function onMouseDown(e){
1818 Roo.log("on Mouse Up");
1820 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1821 Roo.log("MenuManager hideAll");
1830 function onBeforeCheck(mi, state){
1832 var g = groups[mi.group];
1833 for(var i = 0, l = g.length; i < l; i++){
1835 g[i].setChecked(false);
1844 * Hides all menus that are currently visible
1846 hideAll : function(){
1851 register : function(menu){
1855 menus[menu.id] = menu;
1856 menu.on("beforehide", onBeforeHide);
1857 menu.on("hide", onHide);
1858 menu.on("beforeshow", onBeforeShow);
1859 menu.on("show", onShow);
1861 if(g && menu.events["checkchange"]){
1865 groups[g].push(menu);
1866 menu.on("checkchange", onCheck);
1871 * Returns a {@link Roo.menu.Menu} object
1872 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1873 * be used to generate and return a new Menu instance.
1875 get : function(menu){
1876 if(typeof menu == "string"){ // menu id
1878 }else if(menu.events){ // menu instance
1881 /*else if(typeof menu.length == 'number'){ // array of menu items?
1882 return new Roo.bootstrap.Menu({items:menu});
1883 }else{ // otherwise, must be a config
1884 return new Roo.bootstrap.Menu(menu);
1891 unregister : function(menu){
1892 delete menus[menu.id];
1893 menu.un("beforehide", onBeforeHide);
1894 menu.un("hide", onHide);
1895 menu.un("beforeshow", onBeforeShow);
1896 menu.un("show", onShow);
1898 if(g && menu.events["checkchange"]){
1899 groups[g].remove(menu);
1900 menu.un("checkchange", onCheck);
1905 registerCheckable : function(menuItem){
1906 var g = menuItem.group;
1911 groups[g].push(menuItem);
1912 menuItem.on("beforecheckchange", onBeforeCheck);
1917 unregisterCheckable : function(menuItem){
1918 var g = menuItem.group;
1920 groups[g].remove(menuItem);
1921 menuItem.un("beforecheckchange", onBeforeCheck);
1933 * @class Roo.bootstrap.Menu
1934 * @extends Roo.bootstrap.Component
1935 * Bootstrap Menu class - container for MenuItems
1936 * @cfg {String} type (dropdown|treeview|submenu) type of menu
1937 * @cfg {bool} hidden if the menu should be hidden when rendered.
1938 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
1939 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
1943 * @param {Object} config The config object
1947 Roo.bootstrap.Menu = function(config){
1948 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1949 if (this.registerMenu && this.type != 'treeview') {
1950 Roo.bootstrap.MenuMgr.register(this);
1955 * Fires before this menu is displayed
1956 * @param {Roo.menu.Menu} this
1961 * Fires before this menu is hidden
1962 * @param {Roo.menu.Menu} this
1967 * Fires after this menu is displayed
1968 * @param {Roo.menu.Menu} this
1973 * Fires after this menu is hidden
1974 * @param {Roo.menu.Menu} this
1979 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1980 * @param {Roo.menu.Menu} this
1981 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1982 * @param {Roo.EventObject} e
1987 * Fires when the mouse is hovering over this menu
1988 * @param {Roo.menu.Menu} this
1989 * @param {Roo.EventObject} e
1990 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1995 * Fires when the mouse exits this menu
1996 * @param {Roo.menu.Menu} this
1997 * @param {Roo.EventObject} e
1998 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2003 * Fires when a menu item contained in this menu is clicked
2004 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2005 * @param {Roo.EventObject} e
2009 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2012 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
2016 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
2019 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2021 registerMenu : true,
2023 menuItems :false, // stores the menu items..
2033 getChildContainer : function() {
2037 getAutoCreate : function(){
2039 //if (['right'].indexOf(this.align)!==-1) {
2040 // cfg.cn[1].cls += ' pull-right'
2046 cls : 'dropdown-menu' ,
2047 style : 'z-index:1000'
2051 if (this.type === 'submenu') {
2052 cfg.cls = 'submenu active';
2054 if (this.type === 'treeview') {
2055 cfg.cls = 'treeview-menu';
2060 initEvents : function() {
2062 // Roo.log("ADD event");
2063 // Roo.log(this.triggerEl.dom);
2065 this.triggerEl.on('click', this.onTriggerClick, this);
2067 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2069 this.triggerEl.addClass('dropdown-toggle');
2072 this.el.on('touchstart' , this.onTouch, this);
2074 this.el.on('click' , this.onClick, this);
2076 this.el.on("mouseover", this.onMouseOver, this);
2077 this.el.on("mouseout", this.onMouseOut, this);
2081 findTargetItem : function(e)
2083 var t = e.getTarget(".dropdown-menu-item", this.el, true);
2087 //Roo.log(t); Roo.log(t.id);
2089 //Roo.log(this.menuitems);
2090 return this.menuitems.get(t.id);
2092 //return this.items.get(t.menuItemId);
2098 onTouch : function(e)
2100 Roo.log("menu.onTouch");
2101 //e.stopEvent(); this make the user popdown broken
2105 onClick : function(e)
2107 Roo.log("menu.onClick");
2109 var t = this.findTargetItem(e);
2110 if(!t || t.isContainer){
2115 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
2116 if(t == this.activeItem && t.shouldDeactivate(e)){
2117 this.activeItem.deactivate();
2118 delete this.activeItem;
2122 this.setActiveItem(t, true);
2130 Roo.log('pass click event');
2134 this.fireEvent("click", this, t, e);
2138 if(!t.href.length || t.href == '#'){
2139 (function() { _this.hide(); }).defer(100);
2144 onMouseOver : function(e){
2145 var t = this.findTargetItem(e);
2148 // if(t.canActivate && !t.disabled){
2149 // this.setActiveItem(t, true);
2153 this.fireEvent("mouseover", this, e, t);
2155 isVisible : function(){
2156 return !this.hidden;
2158 onMouseOut : function(e){
2159 var t = this.findTargetItem(e);
2162 // if(t == this.activeItem && t.shouldDeactivate(e)){
2163 // this.activeItem.deactivate();
2164 // delete this.activeItem;
2167 this.fireEvent("mouseout", this, e, t);
2172 * Displays this menu relative to another element
2173 * @param {String/HTMLElement/Roo.Element} element The element to align to
2174 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2175 * the element (defaults to this.defaultAlign)
2176 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2178 show : function(el, pos, parentMenu){
2179 this.parentMenu = parentMenu;
2183 this.fireEvent("beforeshow", this);
2184 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2187 * Displays this menu at a specific xy position
2188 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2189 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2191 showAt : function(xy, parentMenu, /* private: */_e){
2192 this.parentMenu = parentMenu;
2197 this.fireEvent("beforeshow", this);
2198 //xy = this.el.adjustForConstraints(xy);
2202 this.hideMenuItems();
2203 this.hidden = false;
2204 this.triggerEl.addClass('open');
2206 if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2207 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2210 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2215 this.fireEvent("show", this);
2221 this.doFocus.defer(50, this);
2225 doFocus : function(){
2227 this.focusEl.focus();
2232 * Hides this menu and optionally all parent menus
2233 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2235 hide : function(deep)
2238 this.hideMenuItems();
2239 if(this.el && this.isVisible()){
2240 this.fireEvent("beforehide", this);
2241 if(this.activeItem){
2242 this.activeItem.deactivate();
2243 this.activeItem = null;
2245 this.triggerEl.removeClass('open');;
2247 this.fireEvent("hide", this);
2249 if(deep === true && this.parentMenu){
2250 this.parentMenu.hide(true);
2254 onTriggerClick : function(e)
2256 Roo.log('trigger click');
2258 var target = e.getTarget();
2260 Roo.log(target.nodeName.toLowerCase());
2262 if(target.nodeName.toLowerCase() === 'i'){
2268 onTriggerPress : function(e)
2270 Roo.log('trigger press');
2271 //Roo.log(e.getTarget());
2272 // Roo.log(this.triggerEl.dom);
2274 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2275 var pel = Roo.get(e.getTarget());
2276 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2277 Roo.log('is treeview or dropdown?');
2281 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2285 if (this.isVisible()) {
2290 this.show(this.triggerEl, false, false);
2293 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2300 hideMenuItems : function()
2302 Roo.log("hide Menu Items");
2306 //$(backdrop).remove()
2307 this.el.select('.open',true).each(function(aa) {
2309 aa.removeClass('open');
2310 //var parent = getParent($(this))
2311 //var relatedTarget = { relatedTarget: this }
2313 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2314 //if (e.isDefaultPrevented()) return
2315 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2318 addxtypeChild : function (tree, cntr) {
2319 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2321 this.menuitems.add(comp);
2342 * @class Roo.bootstrap.MenuItem
2343 * @extends Roo.bootstrap.Component
2344 * Bootstrap MenuItem class
2345 * @cfg {String} html the menu label
2346 * @cfg {String} href the link
2347 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2348 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2349 * @cfg {Boolean} active used on sidebars to highlight active itesm
2350 * @cfg {String} fa favicon to show on left of menu item.
2351 * @cfg {Roo.bootsrap.Menu} menu the child menu.
2355 * Create a new MenuItem
2356 * @param {Object} config The config object
2360 Roo.bootstrap.MenuItem = function(config){
2361 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2366 * The raw click event for the entire grid.
2367 * @param {Roo.bootstrap.MenuItem} this
2368 * @param {Roo.EventObject} e
2374 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2378 preventDefault: false,
2379 isContainer : false,
2383 getAutoCreate : function(){
2385 if(this.isContainer){
2388 cls: 'dropdown-menu-item'
2402 if (this.fa !== false) {
2405 cls : 'fa fa-' + this.fa
2414 cls: 'dropdown-menu-item',
2417 if (this.parent().type == 'treeview') {
2418 cfg.cls = 'treeview-menu';
2421 cfg.cls += ' active';
2426 anc.href = this.href || cfg.cn[0].href ;
2427 ctag.html = this.html || cfg.cn[0].html ;
2431 initEvents: function()
2433 if (this.parent().type == 'treeview') {
2434 this.el.select('a').on('click', this.onClick, this);
2438 this.menu.parentType = this.xtype;
2439 this.menu.triggerEl = this.el;
2440 this.menu = this.addxtype(Roo.apply({}, this.menu));
2444 onClick : function(e)
2446 Roo.log('item on click ');
2448 if(this.preventDefault){
2451 //this.parent().hideMenuItems();
2453 this.fireEvent('click', this, e);
2472 * @class Roo.bootstrap.MenuSeparator
2473 * @extends Roo.bootstrap.Component
2474 * Bootstrap MenuSeparator class
2477 * Create a new MenuItem
2478 * @param {Object} config The config object
2482 Roo.bootstrap.MenuSeparator = function(config){
2483 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2486 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2488 getAutoCreate : function(){
2507 * @class Roo.bootstrap.Modal
2508 * @extends Roo.bootstrap.Component
2509 * Bootstrap Modal class
2510 * @cfg {String} title Title of dialog
2511 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2512 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2513 * @cfg {Boolean} specificTitle default false
2514 * @cfg {Array} buttons Array of buttons or standard button set..
2515 * @cfg {String} buttonPosition (left|right|center) default right
2516 * @cfg {Boolean} animate default true
2517 * @cfg {Boolean} allow_close default true
2518 * @cfg {Boolean} fitwindow default false
2519 * @cfg {String} size (sm|lg) default empty
2523 * Create a new Modal Dialog
2524 * @param {Object} config The config object
2527 Roo.bootstrap.Modal = function(config){
2528 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2533 * The raw btnclick event for the button
2534 * @param {Roo.EventObject} e
2539 * Fire when dialog resize
2540 * @param {Roo.bootstrap.Modal} this
2541 * @param {Roo.EventObject} e
2545 this.buttons = this.buttons || [];
2548 this.tmpl = Roo.factory(this.tmpl);
2553 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2555 title : 'test dialog',
2565 specificTitle: false,
2567 buttonPosition: 'right',
2586 onRender : function(ct, position)
2588 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2591 var cfg = Roo.apply({}, this.getAutoCreate());
2594 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2596 //if (!cfg.name.length) {
2600 cfg.cls += ' ' + this.cls;
2603 cfg.style = this.style;
2605 this.el = Roo.get(document.body).createChild(cfg, position);
2607 //var type = this.el.dom.type;
2610 if(this.tabIndex !== undefined){
2611 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2614 this.dialogEl = this.el.select('.modal-dialog',true).first();
2615 this.bodyEl = this.el.select('.modal-body',true).first();
2616 this.closeEl = this.el.select('.modal-header .close', true).first();
2617 this.headerEl = this.el.select('.modal-header',true).first();
2618 this.titleEl = this.el.select('.modal-title',true).first();
2619 this.footerEl = this.el.select('.modal-footer',true).first();
2621 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2622 this.maskEl.enableDisplayMode("block");
2624 //this.el.addClass("x-dlg-modal");
2626 if (this.buttons.length) {
2627 Roo.each(this.buttons, function(bb) {
2628 var b = Roo.apply({}, bb);
2629 b.xns = b.xns || Roo.bootstrap;
2630 b.xtype = b.xtype || 'Button';
2631 if (typeof(b.listeners) == 'undefined') {
2632 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2635 var btn = Roo.factory(b);
2637 btn.render(this.el.select('.modal-footer div').first());
2641 // render the children.
2644 if(typeof(this.items) != 'undefined'){
2645 var items = this.items;
2648 for(var i =0;i < items.length;i++) {
2649 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2653 this.items = nitems;
2655 // where are these used - they used to be body/close/footer
2659 //this.el.addClass([this.fieldClass, this.cls]);
2663 getAutoCreate : function(){
2668 html : this.html || ''
2673 cls : 'modal-title',
2677 if(this.specificTitle){
2683 if (this.allow_close) {
2695 if(this.size.length){
2696 size = 'modal-' + this.size;
2701 style : 'display: none',
2704 cls: "modal-dialog " + size,
2707 cls : "modal-content",
2710 cls : 'modal-header',
2715 cls : 'modal-footer',
2719 cls: 'btn-' + this.buttonPosition
2736 modal.cls += ' fade';
2742 getChildContainer : function() {
2747 getButtonContainer : function() {
2748 return this.el.select('.modal-footer div',true).first();
2751 initEvents : function()
2753 if (this.allow_close) {
2754 this.closeEl.on('click', this.hide, this);
2756 Roo.EventManager.onWindowResize(this.resize, this, true);
2763 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2764 if (this.fitwindow) {
2765 var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2766 var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2771 setSize : function(w,h)
2781 if (!this.rendered) {
2785 this.el.setStyle('display', 'block');
2787 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
2790 this.el.addClass('in');
2793 this.el.addClass('in');
2797 // not sure how we can show data in here..
2799 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2802 Roo.get(document.body).addClass("x-body-masked");
2804 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2805 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2810 this.fireEvent('show', this);
2812 // set zindex here - otherwise it appears to be ignored...
2813 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2816 this.items.forEach( function(e) {
2817 e.layout ? e.layout() : false;
2825 if(this.fireEvent("beforehide", this) !== false){
2827 Roo.get(document.body).removeClass("x-body-masked");
2828 this.el.removeClass('in');
2829 this.el.select('.modal-dialog', true).first().setStyle('transform','');
2831 if(this.animate){ // why
2833 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2835 this.el.setStyle('display', 'none');
2837 this.fireEvent('hide', this);
2841 addButton : function(str, cb)
2845 var b = Roo.apply({}, { html : str } );
2846 b.xns = b.xns || Roo.bootstrap;
2847 b.xtype = b.xtype || 'Button';
2848 if (typeof(b.listeners) == 'undefined') {
2849 b.listeners = { click : cb.createDelegate(this) };
2852 var btn = Roo.factory(b);
2854 btn.render(this.el.select('.modal-footer div').first());
2860 setDefaultButton : function(btn)
2862 //this.el.select('.modal-footer').()
2866 resizeTo: function(w,h)
2870 this.dialogEl.setWidth(w);
2871 if (this.diff === false) {
2872 this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2875 this.bodyEl.setHeight(h-this.diff);
2877 this.fireEvent('resize', this);
2880 setContentSize : function(w, h)
2884 onButtonClick: function(btn,e)
2887 this.fireEvent('btnclick', btn.name, e);
2890 * Set the title of the Dialog
2891 * @param {String} str new Title
2893 setTitle: function(str) {
2894 this.titleEl.dom.innerHTML = str;
2897 * Set the body of the Dialog
2898 * @param {String} str new Title
2900 setBody: function(str) {
2901 this.bodyEl.dom.innerHTML = str;
2904 * Set the body of the Dialog using the template
2905 * @param {Obj} data - apply this data to the template and replace the body contents.
2907 applyBody: function(obj)
2910 Roo.log("Error - using apply Body without a template");
2913 this.tmpl.overwrite(this.bodyEl, obj);
2919 Roo.apply(Roo.bootstrap.Modal, {
2921 * Button config that displays a single OK button
2930 * Button config that displays Yes and No buttons
2946 * Button config that displays OK and Cancel buttons
2961 * Button config that displays Yes, No and Cancel buttons
2985 * messagebox - can be used as a replace
2989 * @class Roo.MessageBox
2990 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
2994 Roo.Msg.alert('Status', 'Changes saved successfully.');
2996 // Prompt for user data:
2997 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2999 // process text value...
3003 // Show a dialog using config options:
3005 title:'Save Changes?',
3006 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3007 buttons: Roo.Msg.YESNOCANCEL,
3014 Roo.bootstrap.MessageBox = function(){
3015 var dlg, opt, mask, waitTimer;
3016 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3017 var buttons, activeTextEl, bwidth;
3021 var handleButton = function(button){
3023 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3027 var handleHide = function(){
3029 dlg.el.removeClass(opt.cls);
3032 // Roo.TaskMgr.stop(waitTimer);
3033 // waitTimer = null;
3038 var updateButtons = function(b){
3041 buttons["ok"].hide();
3042 buttons["cancel"].hide();
3043 buttons["yes"].hide();
3044 buttons["no"].hide();
3045 //dlg.footer.dom.style.display = 'none';
3048 dlg.footerEl.dom.style.display = '';
3049 for(var k in buttons){
3050 if(typeof buttons[k] != "function"){
3053 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3054 width += buttons[k].el.getWidth()+15;
3064 var handleEsc = function(d, k, e){
3065 if(opt && opt.closable !== false){
3075 * Returns a reference to the underlying {@link Roo.BasicDialog} element
3076 * @return {Roo.BasicDialog} The BasicDialog element
3078 getDialog : function(){
3080 dlg = new Roo.bootstrap.Modal( {
3083 //constraintoviewport:false,
3085 //collapsible : false,
3090 //buttonAlign:"center",
3091 closeClick : function(){
3092 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3095 handleButton("cancel");
3100 dlg.on("hide", handleHide);
3102 //dlg.addKeyListener(27, handleEsc);
3104 this.buttons = buttons;
3105 var bt = this.buttonText;
3106 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3107 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3108 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3109 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3111 bodyEl = dlg.bodyEl.createChild({
3113 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3114 '<textarea class="roo-mb-textarea"></textarea>' +
3115 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
3117 msgEl = bodyEl.dom.firstChild;
3118 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3119 textboxEl.enableDisplayMode();
3120 textboxEl.addKeyListener([10,13], function(){
3121 if(dlg.isVisible() && opt && opt.buttons){
3124 }else if(opt.buttons.yes){
3125 handleButton("yes");
3129 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3130 textareaEl.enableDisplayMode();
3131 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3132 progressEl.enableDisplayMode();
3134 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3135 var pf = progressEl.dom.firstChild;
3137 pp = Roo.get(pf.firstChild);
3138 pp.setHeight(pf.offsetHeight);
3146 * Updates the message box body text
3147 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3148 * the XHTML-compliant non-breaking space character '&#160;')
3149 * @return {Roo.MessageBox} This message box
3151 updateText : function(text)
3153 if(!dlg.isVisible() && !opt.width){
3154 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3155 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3157 msgEl.innerHTML = text || ' ';
3159 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3160 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3162 Math.min(opt.width || cw , this.maxWidth),
3163 Math.max(opt.minWidth || this.minWidth, bwidth)
3166 activeTextEl.setWidth(w);
3168 if(dlg.isVisible()){
3169 dlg.fixedcenter = false;
3171 // to big, make it scroll. = But as usual stupid IE does not support
3174 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3175 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3176 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3178 bodyEl.dom.style.height = '';
3179 bodyEl.dom.style.overflowY = '';
3182 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3184 bodyEl.dom.style.overflowX = '';
3187 dlg.setContentSize(w, bodyEl.getHeight());
3188 if(dlg.isVisible()){
3189 dlg.fixedcenter = true;
3195 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
3196 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3197 * @param {Number} value Any number between 0 and 1 (e.g., .5)
3198 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3199 * @return {Roo.MessageBox} This message box
3201 updateProgress : function(value, text){
3203 this.updateText(text);
3206 if (pp) { // weird bug on my firefox - for some reason this is not defined
3207 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3208 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3214 * Returns true if the message box is currently displayed
3215 * @return {Boolean} True if the message box is visible, else false
3217 isVisible : function(){
3218 return dlg && dlg.isVisible();
3222 * Hides the message box if it is displayed
3225 if(this.isVisible()){
3231 * Displays a new message box, or reinitializes an existing message box, based on the config options
3232 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3233 * The following config object properties are supported:
3235 Property Type Description
3236 ---------- --------------- ------------------------------------------------------------------------------------
3237 animEl String/Element An id or Element from which the message box should animate as it opens and
3238 closes (defaults to undefined)
3239 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3240 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3241 closable Boolean False to hide the top-right close button (defaults to true). Note that
3242 progress and wait dialogs will ignore this property and always hide the
3243 close button as they can only be closed programmatically.
3244 cls String A custom CSS class to apply to the message box element
3245 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3246 displayed (defaults to 75)
3247 fn Function A callback function to execute after closing the dialog. The arguments to the
3248 function will be btn (the name of the button that was clicked, if applicable,
3249 e.g. "ok"), and text (the value of the active text field, if applicable).
3250 Progress and wait dialogs will ignore this option since they do not respond to
3251 user actions and can only be closed programmatically, so any required function
3252 should be called by the same code after it closes the dialog.
3253 icon String A CSS class that provides a background image to be used as an icon for
3254 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3255 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3256 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3257 modal Boolean False to allow user interaction with the page while the message box is
3258 displayed (defaults to true)
3259 msg String A string that will replace the existing message box body text (defaults
3260 to the XHTML-compliant non-breaking space character ' ')
3261 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3262 progress Boolean True to display a progress bar (defaults to false)
3263 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3264 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3265 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3266 title String The title text
3267 value String The string value to set into the active textbox element if displayed
3268 wait Boolean True to display a progress bar (defaults to false)
3269 width Number The width of the dialog in pixels
3276 msg: 'Please enter your address:',
3278 buttons: Roo.MessageBox.OKCANCEL,
3281 animEl: 'addAddressBtn'
3284 * @param {Object} config Configuration options
3285 * @return {Roo.MessageBox} This message box
3287 show : function(options)
3290 // this causes nightmares if you show one dialog after another
3291 // especially on callbacks..
3293 if(this.isVisible()){
3296 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3297 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3298 Roo.log("New Dialog Message:" + options.msg )
3299 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3300 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3303 var d = this.getDialog();
3305 d.setTitle(opt.title || " ");
3306 d.closeEl.setDisplayed(opt.closable !== false);
3307 activeTextEl = textboxEl;
3308 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3313 textareaEl.setHeight(typeof opt.multiline == "number" ?
3314 opt.multiline : this.defaultTextHeight);
3315 activeTextEl = textareaEl;
3324 progressEl.setDisplayed(opt.progress === true);
3325 this.updateProgress(0);
3326 activeTextEl.dom.value = opt.value || "";
3328 dlg.setDefaultButton(activeTextEl);
3330 var bs = opt.buttons;
3334 }else if(bs && bs.yes){
3335 db = buttons["yes"];
3337 dlg.setDefaultButton(db);
3339 bwidth = updateButtons(opt.buttons);
3340 this.updateText(opt.msg);
3342 d.el.addClass(opt.cls);
3344 d.proxyDrag = opt.proxyDrag === true;
3345 d.modal = opt.modal !== false;
3346 d.mask = opt.modal !== false ? mask : false;
3348 // force it to the end of the z-index stack so it gets a cursor in FF
3349 document.body.appendChild(dlg.el.dom);
3350 d.animateTarget = null;
3351 d.show(options.animEl);
3357 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3358 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3359 * and closing the message box when the process is complete.
3360 * @param {String} title The title bar text
3361 * @param {String} msg The message box body text
3362 * @return {Roo.MessageBox} This message box
3364 progress : function(title, msg){
3371 minWidth: this.minProgressWidth,
3378 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3379 * If a callback function is passed it will be called after the user clicks the button, and the
3380 * id of the button that was clicked will be passed as the only parameter to the callback
3381 * (could also be the top-right close button).
3382 * @param {String} title The title bar text
3383 * @param {String} msg The message box body text
3384 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3385 * @param {Object} scope (optional) The scope of the callback function
3386 * @return {Roo.MessageBox} This message box
3388 alert : function(title, msg, fn, scope)
3403 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3404 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3405 * You are responsible for closing the message box when the process is complete.
3406 * @param {String} msg The message box body text
3407 * @param {String} title (optional) The title bar text
3408 * @return {Roo.MessageBox} This message box
3410 wait : function(msg, title){
3421 waitTimer = Roo.TaskMgr.start({
3423 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3431 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3432 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3433 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3434 * @param {String} title The title bar text
3435 * @param {String} msg The message box body text
3436 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3437 * @param {Object} scope (optional) The scope of the callback function
3438 * @return {Roo.MessageBox} This message box
3440 confirm : function(title, msg, fn, scope){
3444 buttons: this.YESNO,
3453 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3454 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3455 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3456 * (could also be the top-right close button) and the text that was entered will be passed as the two
3457 * parameters to the callback.
3458 * @param {String} title The title bar text
3459 * @param {String} msg The message box body text
3460 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3461 * @param {Object} scope (optional) The scope of the callback function
3462 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3463 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3464 * @return {Roo.MessageBox} This message box
3466 prompt : function(title, msg, fn, scope, multiline){
3470 buttons: this.OKCANCEL,
3475 multiline: multiline,
3482 * Button config that displays a single OK button
3487 * Button config that displays Yes and No buttons
3490 YESNO : {yes:true, no:true},
3492 * Button config that displays OK and Cancel buttons
3495 OKCANCEL : {ok:true, cancel:true},
3497 * Button config that displays Yes, No and Cancel buttons
3500 YESNOCANCEL : {yes:true, no:true, cancel:true},
3503 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3506 defaultTextHeight : 75,
3508 * The maximum width in pixels of the message box (defaults to 600)
3513 * The minimum width in pixels of the message box (defaults to 100)
3518 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3519 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3522 minProgressWidth : 250,
3524 * An object containing the default button text strings that can be overriden for localized language support.
3525 * Supported properties are: ok, cancel, yes and no.
3526 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3539 * Shorthand for {@link Roo.MessageBox}
3541 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3542 Roo.Msg = Roo.Msg || Roo.MessageBox;
3551 * @class Roo.bootstrap.Navbar
3552 * @extends Roo.bootstrap.Component
3553 * Bootstrap Navbar class
3556 * Create a new Navbar
3557 * @param {Object} config The config object
3561 Roo.bootstrap.Navbar = function(config){
3562 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3566 * @event beforetoggle
3567 * Fire before toggle the menu
3568 * @param {Roo.EventObject} e
3570 "beforetoggle" : true
3574 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3583 getAutoCreate : function(){
3586 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3590 initEvents :function ()
3592 //Roo.log(this.el.select('.navbar-toggle',true));
3593 this.el.select('.navbar-toggle',true).on('click', function() {
3594 if(this.fireEvent('beforetoggle', this) !== false){
3595 this.el.select('.navbar-collapse',true).toggleClass('in');
3605 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3607 var size = this.el.getSize();
3608 this.maskEl.setSize(size.width, size.height);
3609 this.maskEl.enableDisplayMode("block");
3618 getChildContainer : function()
3620 if (this.el.select('.collapse').getCount()) {
3621 return this.el.select('.collapse',true).first();
3654 * @class Roo.bootstrap.NavSimplebar
3655 * @extends Roo.bootstrap.Navbar
3656 * Bootstrap Sidebar class
3658 * @cfg {Boolean} inverse is inverted color
3660 * @cfg {String} type (nav | pills | tabs)
3661 * @cfg {Boolean} arrangement stacked | justified
3662 * @cfg {String} align (left | right) alignment
3664 * @cfg {Boolean} main (true|false) main nav bar? default false
3665 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3667 * @cfg {String} tag (header|footer|nav|div) default is nav
3673 * Create a new Sidebar
3674 * @param {Object} config The config object
3678 Roo.bootstrap.NavSimplebar = function(config){
3679 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3682 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3698 getAutoCreate : function(){
3702 tag : this.tag || 'div',
3715 this.type = this.type || 'nav';
3716 if (['tabs','pills'].indexOf(this.type)!==-1) {
3717 cfg.cn[0].cls += ' nav-' + this.type
3721 if (this.type!=='nav') {
3722 Roo.log('nav type must be nav/tabs/pills')
3724 cfg.cn[0].cls += ' navbar-nav'
3730 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3731 cfg.cn[0].cls += ' nav-' + this.arrangement;
3735 if (this.align === 'right') {
3736 cfg.cn[0].cls += ' navbar-right';
3740 cfg.cls += ' navbar-inverse';
3767 * @class Roo.bootstrap.NavHeaderbar
3768 * @extends Roo.bootstrap.NavSimplebar
3769 * Bootstrap Sidebar class
3771 * @cfg {String} brand what is brand
3772 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3773 * @cfg {String} brand_href href of the brand
3774 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
3775 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3776 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3777 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3780 * Create a new Sidebar
3781 * @param {Object} config The config object
3785 Roo.bootstrap.NavHeaderbar = function(config){
3786 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3790 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3797 desktopCenter : false,
3800 getAutoCreate : function(){
3803 tag: this.nav || 'nav',
3810 if (this.desktopCenter) {
3811 cn.push({cls : 'container', cn : []});
3818 cls: 'navbar-header',
3823 cls: 'navbar-toggle',
3824 'data-toggle': 'collapse',
3829 html: 'Toggle navigation'
3851 cls: 'collapse navbar-collapse',
3855 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3857 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3858 cfg.cls += ' navbar-' + this.position;
3860 // tag can override this..
3862 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
3865 if (this.brand !== '') {
3868 href: this.brand_href ? this.brand_href : '#',
3869 cls: 'navbar-brand',
3877 cfg.cls += ' main-nav';
3885 getHeaderChildContainer : function()
3887 if (this.srButton && this.el.select('.navbar-header').getCount()) {
3888 return this.el.select('.navbar-header',true).first();
3891 return this.getChildContainer();
3895 initEvents : function()
3897 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3899 if (this.autohide) {
3904 Roo.get(document).on('scroll',function(e) {
3905 var ns = Roo.get(document).getScroll().top;
3906 var os = prevScroll;
3910 ft.removeClass('slideDown');
3911 ft.addClass('slideUp');
3914 ft.removeClass('slideUp');
3915 ft.addClass('slideDown');
3936 * @class Roo.bootstrap.NavSidebar
3937 * @extends Roo.bootstrap.Navbar
3938 * Bootstrap Sidebar class
3941 * Create a new Sidebar
3942 * @param {Object} config The config object
3946 Roo.bootstrap.NavSidebar = function(config){
3947 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3950 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
3952 sidebar : true, // used by Navbar Item and NavbarGroup at present...
3954 getAutoCreate : function(){
3959 cls: 'sidebar sidebar-nav'
3981 * @class Roo.bootstrap.NavGroup
3982 * @extends Roo.bootstrap.Component
3983 * Bootstrap NavGroup class
3984 * @cfg {String} align (left|right)
3985 * @cfg {Boolean} inverse
3986 * @cfg {String} type (nav|pills|tab) default nav
3987 * @cfg {String} navId - reference Id for navbar.
3991 * Create a new nav group
3992 * @param {Object} config The config object
3995 Roo.bootstrap.NavGroup = function(config){
3996 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3999 Roo.bootstrap.NavGroup.register(this);
4003 * Fires when the active item changes
4004 * @param {Roo.bootstrap.NavGroup} this
4005 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4006 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
4013 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
4024 getAutoCreate : function()
4026 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4033 if (['tabs','pills'].indexOf(this.type)!==-1) {
4034 cfg.cls += ' nav-' + this.type
4036 if (this.type!=='nav') {
4037 Roo.log('nav type must be nav/tabs/pills')
4039 cfg.cls += ' navbar-nav'
4042 if (this.parent() && this.parent().sidebar) {
4045 cls: 'dashboard-menu sidebar-menu'
4051 if (this.form === true) {
4057 if (this.align === 'right') {
4058 cfg.cls += ' navbar-right';
4060 cfg.cls += ' navbar-left';
4064 if (this.align === 'right') {
4065 cfg.cls += ' navbar-right';
4069 cfg.cls += ' navbar-inverse';
4077 * sets the active Navigation item
4078 * @param {Roo.bootstrap.NavItem} the new current navitem
4080 setActiveItem : function(item)
4083 Roo.each(this.navItems, function(v){
4088 v.setActive(false, true);
4095 item.setActive(true, true);
4096 this.fireEvent('changed', this, item, prev);
4101 * gets the active Navigation item
4102 * @return {Roo.bootstrap.NavItem} the current navitem
4104 getActive : function()
4108 Roo.each(this.navItems, function(v){
4119 indexOfNav : function()
4123 Roo.each(this.navItems, function(v,i){
4134 * adds a Navigation item
4135 * @param {Roo.bootstrap.NavItem} the navitem to add
4137 addItem : function(cfg)
4139 var cn = new Roo.bootstrap.NavItem(cfg);
4141 cn.parentId = this.id;
4142 cn.onRender(this.el, null);
4146 * register a Navigation item
4147 * @param {Roo.bootstrap.NavItem} the navitem to add
4149 register : function(item)
4151 this.navItems.push( item);
4152 item.navId = this.navId;
4157 * clear all the Navigation item
4160 clearAll : function()
4163 this.el.dom.innerHTML = '';
4166 getNavItem: function(tabId)
4169 Roo.each(this.navItems, function(e) {
4170 if (e.tabId == tabId) {
4180 setActiveNext : function()
4182 var i = this.indexOfNav(this.getActive());
4183 if (i > this.navItems.length) {
4186 this.setActiveItem(this.navItems[i+1]);
4188 setActivePrev : function()
4190 var i = this.indexOfNav(this.getActive());
4194 this.setActiveItem(this.navItems[i-1]);
4196 clearWasActive : function(except) {
4197 Roo.each(this.navItems, function(e) {
4198 if (e.tabId != except.tabId && e.was_active) {
4199 e.was_active = false;
4206 getWasActive : function ()
4209 Roo.each(this.navItems, function(e) {
4224 Roo.apply(Roo.bootstrap.NavGroup, {
4228 * register a Navigation Group
4229 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4231 register : function(navgrp)
4233 this.groups[navgrp.navId] = navgrp;
4237 * fetch a Navigation Group based on the navigation ID
4238 * @param {string} the navgroup to add
4239 * @returns {Roo.bootstrap.NavGroup} the navgroup
4241 get: function(navId) {
4242 if (typeof(this.groups[navId]) == 'undefined') {
4244 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4246 return this.groups[navId] ;
4261 * @class Roo.bootstrap.NavItem
4262 * @extends Roo.bootstrap.Component
4263 * Bootstrap Navbar.NavItem class
4264 * @cfg {String} href link to
4265 * @cfg {String} html content of button
4266 * @cfg {String} badge text inside badge
4267 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4268 * @cfg {String} glyphicon name of glyphicon
4269 * @cfg {String} icon name of font awesome icon
4270 * @cfg {Boolean} active Is item active
4271 * @cfg {Boolean} disabled Is item disabled
4273 * @cfg {Boolean} preventDefault (true | false) default false
4274 * @cfg {String} tabId the tab that this item activates.
4275 * @cfg {String} tagtype (a|span) render as a href or span?
4276 * @cfg {Boolean} animateRef (true|false) link to element default false
4279 * Create a new Navbar Item
4280 * @param {Object} config The config object
4282 Roo.bootstrap.NavItem = function(config){
4283 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4288 * The raw click event for the entire grid.
4289 * @param {Roo.EventObject} e
4294 * Fires when the active item active state changes
4295 * @param {Roo.bootstrap.NavItem} this
4296 * @param {boolean} state the new state
4302 * Fires when scroll to element
4303 * @param {Roo.bootstrap.NavItem} this
4304 * @param {Object} options
4305 * @param {Roo.EventObject} e
4313 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4321 preventDefault : false,
4328 getAutoCreate : function(){
4337 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4339 if (this.disabled) {
4340 cfg.cls += ' disabled';
4343 if (this.href || this.html || this.glyphicon || this.icon) {
4347 href : this.href || "#",
4348 html: this.html || ''
4353 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4356 if(this.glyphicon) {
4357 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4362 cfg.cn[0].html += " <span class='caret'></span>";
4366 if (this.badge !== '') {
4368 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4376 initEvents: function()
4378 if (typeof (this.menu) != 'undefined') {
4379 this.menu.parentType = this.xtype;
4380 this.menu.triggerEl = this.el;
4381 this.menu = this.addxtype(Roo.apply({}, this.menu));
4384 this.el.select('a',true).on('click', this.onClick, this);
4386 if(this.tagtype == 'span'){
4387 this.el.select('span',true).on('click', this.onClick, this);
4390 // at this point parent should be available..
4391 this.parent().register(this);
4394 onClick : function(e)
4396 if (e.getTarget('.dropdown-menu-item')) {
4397 // did you click on a menu itemm.... - then don't trigger onclick..
4402 this.preventDefault ||
4405 Roo.log("NavItem - prevent Default?");
4409 if (this.disabled) {
4413 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4414 if (tg && tg.transition) {
4415 Roo.log("waiting for the transitionend");
4421 //Roo.log("fire event clicked");
4422 if(this.fireEvent('click', this, e) === false){
4426 if(this.tagtype == 'span'){
4430 //Roo.log(this.href);
4431 var ael = this.el.select('a',true).first();
4434 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4435 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4436 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4437 return; // ignore... - it's a 'hash' to another page.
4439 Roo.log("NavItem - prevent Default?");
4441 this.scrollToElement(e);
4445 var p = this.parent();
4447 if (['tabs','pills'].indexOf(p.type)!==-1) {
4448 if (typeof(p.setActiveItem) !== 'undefined') {
4449 p.setActiveItem(this);
4453 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4454 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4455 // remove the collapsed menu expand...
4456 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4460 isActive: function () {
4463 setActive : function(state, fire, is_was_active)
4465 if (this.active && !state && this.navId) {
4466 this.was_active = true;
4467 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4469 nv.clearWasActive(this);
4473 this.active = state;
4476 this.el.removeClass('active');
4477 } else if (!this.el.hasClass('active')) {
4478 this.el.addClass('active');
4481 this.fireEvent('changed', this, state);
4484 // show a panel if it's registered and related..
4486 if (!this.navId || !this.tabId || !state || is_was_active) {
4490 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4494 var pan = tg.getPanelByName(this.tabId);
4498 // if we can not flip to new panel - go back to old nav highlight..
4499 if (false == tg.showPanel(pan)) {
4500 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4502 var onav = nv.getWasActive();
4504 onav.setActive(true, false, true);
4513 // this should not be here...
4514 setDisabled : function(state)
4516 this.disabled = state;
4518 this.el.removeClass('disabled');
4519 } else if (!this.el.hasClass('disabled')) {
4520 this.el.addClass('disabled');
4526 * Fetch the element to display the tooltip on.
4527 * @return {Roo.Element} defaults to this.el
4529 tooltipEl : function()
4531 return this.el.select('' + this.tagtype + '', true).first();
4534 scrollToElement : function(e)
4536 var c = document.body;
4539 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4541 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4542 c = document.documentElement;
4545 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4551 var o = target.calcOffsetsTo(c);
4558 this.fireEvent('scrollto', this, options, e);
4560 Roo.get(c).scrollTo('top', options.value, true);
4573 * <span> icon </span>
4574 * <span> text </span>
4575 * <span>badge </span>
4579 * @class Roo.bootstrap.NavSidebarItem
4580 * @extends Roo.bootstrap.NavItem
4581 * Bootstrap Navbar.NavSidebarItem class
4582 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4583 * {Boolean} open is the menu open
4584 * {Boolean} buttonView use button as the tigger el rather that a (default false)
4585 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4586 * {String} buttonSize (sm|md|lg)the extra classes for the button
4587 * {Boolean} showArrow show arrow next to the text (default true)
4589 * Create a new Navbar Button
4590 * @param {Object} config The config object
4592 Roo.bootstrap.NavSidebarItem = function(config){
4593 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4598 * The raw click event for the entire grid.
4599 * @param {Roo.EventObject} e
4604 * Fires when the active item active state changes
4605 * @param {Roo.bootstrap.NavSidebarItem} this
4606 * @param {boolean} state the new state
4614 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4616 badgeWeight : 'default',
4622 buttonWeight : 'default',
4628 getAutoCreate : function(){
4633 href : this.href || '#',
4639 if(this.buttonView){
4642 href : this.href || '#',
4643 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4656 cfg.cls += ' active';
4659 if (this.disabled) {
4660 cfg.cls += ' disabled';
4663 cfg.cls += ' open x-open';
4666 if (this.glyphicon || this.icon) {
4667 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4668 a.cn.push({ tag : 'i', cls : c }) ;
4671 if(!this.buttonView){
4674 html : this.html || ''
4681 if (this.badge !== '') {
4682 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4688 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4691 a.cls += ' dropdown-toggle treeview' ;
4697 initEvents : function()
4699 if (typeof (this.menu) != 'undefined') {
4700 this.menu.parentType = this.xtype;
4701 this.menu.triggerEl = this.el;
4702 this.menu = this.addxtype(Roo.apply({}, this.menu));
4705 this.el.on('click', this.onClick, this);
4707 if(this.badge !== ''){
4708 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4713 onClick : function(e)
4720 if(this.preventDefault){
4724 this.fireEvent('click', this);
4727 disable : function()
4729 this.setDisabled(true);
4734 this.setDisabled(false);
4737 setDisabled : function(state)
4739 if(this.disabled == state){
4743 this.disabled = state;
4746 this.el.addClass('disabled');
4750 this.el.removeClass('disabled');
4755 setActive : function(state)
4757 if(this.active == state){
4761 this.active = state;
4764 this.el.addClass('active');
4768 this.el.removeClass('active');
4773 isActive: function ()
4778 setBadge : function(str)
4784 this.badgeEl.dom.innerHTML = str;
4801 * @class Roo.bootstrap.Row
4802 * @extends Roo.bootstrap.Component
4803 * Bootstrap Row class (contains columns...)
4807 * @param {Object} config The config object
4810 Roo.bootstrap.Row = function(config){
4811 Roo.bootstrap.Row.superclass.constructor.call(this, config);
4814 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
4816 getAutoCreate : function(){
4835 * @class Roo.bootstrap.Element
4836 * @extends Roo.bootstrap.Component
4837 * Bootstrap Element class
4838 * @cfg {String} html contents of the element
4839 * @cfg {String} tag tag of the element
4840 * @cfg {String} cls class of the element
4841 * @cfg {Boolean} preventDefault (true|false) default false
4842 * @cfg {Boolean} clickable (true|false) default false
4845 * Create a new Element
4846 * @param {Object} config The config object
4849 Roo.bootstrap.Element = function(config){
4850 Roo.bootstrap.Element.superclass.constructor.call(this, config);
4856 * When a element is chick
4857 * @param {Roo.bootstrap.Element} this
4858 * @param {Roo.EventObject} e
4864 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
4869 preventDefault: false,
4872 getAutoCreate : function(){
4883 initEvents: function()
4885 Roo.bootstrap.Element.superclass.initEvents.call(this);
4888 this.el.on('click', this.onClick, this);
4893 onClick : function(e)
4895 if(this.preventDefault){
4899 this.fireEvent('click', this, e);
4902 getValue : function()
4904 return this.el.dom.innerHTML;
4907 setValue : function(value)
4909 this.el.dom.innerHTML = value;
4924 * @class Roo.bootstrap.Pagination
4925 * @extends Roo.bootstrap.Component
4926 * Bootstrap Pagination class
4927 * @cfg {String} size xs | sm | md | lg
4928 * @cfg {Boolean} inverse false | true
4931 * Create a new Pagination
4932 * @param {Object} config The config object
4935 Roo.bootstrap.Pagination = function(config){
4936 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4939 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
4945 getAutoCreate : function(){
4951 cfg.cls += ' inverse';
4957 cfg.cls += " " + this.cls;
4975 * @class Roo.bootstrap.PaginationItem
4976 * @extends Roo.bootstrap.Component
4977 * Bootstrap PaginationItem class
4978 * @cfg {String} html text
4979 * @cfg {String} href the link
4980 * @cfg {Boolean} preventDefault (true | false) default true
4981 * @cfg {Boolean} active (true | false) default false
4982 * @cfg {Boolean} disabled default false
4986 * Create a new PaginationItem
4987 * @param {Object} config The config object
4991 Roo.bootstrap.PaginationItem = function(config){
4992 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4997 * The raw click event for the entire grid.
4998 * @param {Roo.EventObject} e
5004 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
5008 preventDefault: true,
5013 getAutoCreate : function(){
5019 href : this.href ? this.href : '#',
5020 html : this.html ? this.html : ''
5030 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5034 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5040 initEvents: function() {
5042 this.el.on('click', this.onClick, this);
5045 onClick : function(e)
5047 Roo.log('PaginationItem on click ');
5048 if(this.preventDefault){
5056 this.fireEvent('click', this, e);
5072 * @class Roo.bootstrap.Slider
5073 * @extends Roo.bootstrap.Component
5074 * Bootstrap Slider class
5077 * Create a new Slider
5078 * @param {Object} config The config object
5081 Roo.bootstrap.Slider = function(config){
5082 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5085 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
5087 getAutoCreate : function(){
5091 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5095 cls: 'ui-slider-handle ui-state-default ui-corner-all'
5107 * Ext JS Library 1.1.1
5108 * Copyright(c) 2006-2007, Ext JS, LLC.
5110 * Originally Released Under LGPL - original licence link has changed is not relivant.
5113 * <script type="text/javascript">
5118 * @class Roo.grid.ColumnModel
5119 * @extends Roo.util.Observable
5120 * This is the default implementation of a ColumnModel used by the Grid. It defines
5121 * the columns in the grid.
5124 var colModel = new Roo.grid.ColumnModel([
5125 {header: "Ticker", width: 60, sortable: true, locked: true},
5126 {header: "Company Name", width: 150, sortable: true},
5127 {header: "Market Cap.", width: 100, sortable: true},
5128 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5129 {header: "Employees", width: 100, sortable: true, resizable: false}
5134 * The config options listed for this class are options which may appear in each
5135 * individual column definition.
5136 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5138 * @param {Object} config An Array of column config objects. See this class's
5139 * config objects for details.
5141 Roo.grid.ColumnModel = function(config){
5143 * The config passed into the constructor
5145 this.config = config;
5148 // if no id, create one
5149 // if the column does not have a dataIndex mapping,
5150 // map it to the order it is in the config
5151 for(var i = 0, len = config.length; i < len; i++){
5153 if(typeof c.dataIndex == "undefined"){
5156 if(typeof c.renderer == "string"){
5157 c.renderer = Roo.util.Format[c.renderer];
5159 if(typeof c.id == "undefined"){
5162 if(c.editor && c.editor.xtype){
5163 c.editor = Roo.factory(c.editor, Roo.grid);
5165 if(c.editor && c.editor.isFormField){
5166 c.editor = new Roo.grid.GridEditor(c.editor);
5168 this.lookup[c.id] = c;
5172 * The width of columns which have no width specified (defaults to 100)
5175 this.defaultWidth = 100;
5178 * Default sortable of columns which have no sortable specified (defaults to false)
5181 this.defaultSortable = false;
5185 * @event widthchange
5186 * Fires when the width of a column changes.
5187 * @param {ColumnModel} this
5188 * @param {Number} columnIndex The column index
5189 * @param {Number} newWidth The new width
5191 "widthchange": true,
5193 * @event headerchange
5194 * Fires when the text of a header changes.
5195 * @param {ColumnModel} this
5196 * @param {Number} columnIndex The column index
5197 * @param {Number} newText The new header text
5199 "headerchange": true,
5201 * @event hiddenchange
5202 * Fires when a column is hidden or "unhidden".
5203 * @param {ColumnModel} this
5204 * @param {Number} columnIndex The column index
5205 * @param {Boolean} hidden true if hidden, false otherwise
5207 "hiddenchange": true,
5209 * @event columnmoved
5210 * Fires when a column is moved.
5211 * @param {ColumnModel} this
5212 * @param {Number} oldIndex
5213 * @param {Number} newIndex
5215 "columnmoved" : true,
5217 * @event columlockchange
5218 * Fires when a column's locked state is changed
5219 * @param {ColumnModel} this
5220 * @param {Number} colIndex
5221 * @param {Boolean} locked true if locked
5223 "columnlockchange" : true
5225 Roo.grid.ColumnModel.superclass.constructor.call(this);
5227 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5229 * @cfg {String} header The header text to display in the Grid view.
5232 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5233 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5234 * specified, the column's index is used as an index into the Record's data Array.
5237 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5238 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5241 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5242 * Defaults to the value of the {@link #defaultSortable} property.
5243 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5246 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5249 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5252 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5255 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5258 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5259 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5260 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5261 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5264 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5267 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5270 * @cfg {String} cursor (Optional)
5273 * @cfg {String} tooltip (Optional)
5276 * @cfg {Number} xs (Optional)
5279 * @cfg {Number} sm (Optional)
5282 * @cfg {Number} md (Optional)
5285 * @cfg {Number} lg (Optional)
5288 * Returns the id of the column at the specified index.
5289 * @param {Number} index The column index
5290 * @return {String} the id
5292 getColumnId : function(index){
5293 return this.config[index].id;
5297 * Returns the column for a specified id.
5298 * @param {String} id The column id
5299 * @return {Object} the column
5301 getColumnById : function(id){
5302 return this.lookup[id];
5307 * Returns the column for a specified dataIndex.
5308 * @param {String} dataIndex The column dataIndex
5309 * @return {Object|Boolean} the column or false if not found
5311 getColumnByDataIndex: function(dataIndex){
5312 var index = this.findColumnIndex(dataIndex);
5313 return index > -1 ? this.config[index] : false;
5317 * Returns the index for a specified column id.
5318 * @param {String} id The column id
5319 * @return {Number} the index, or -1 if not found
5321 getIndexById : function(id){
5322 for(var i = 0, len = this.config.length; i < len; i++){
5323 if(this.config[i].id == id){
5331 * Returns the index for a specified column dataIndex.
5332 * @param {String} dataIndex The column dataIndex
5333 * @return {Number} the index, or -1 if not found
5336 findColumnIndex : function(dataIndex){
5337 for(var i = 0, len = this.config.length; i < len; i++){
5338 if(this.config[i].dataIndex == dataIndex){
5346 moveColumn : function(oldIndex, newIndex){
5347 var c = this.config[oldIndex];
5348 this.config.splice(oldIndex, 1);
5349 this.config.splice(newIndex, 0, c);
5350 this.dataMap = null;
5351 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5354 isLocked : function(colIndex){
5355 return this.config[colIndex].locked === true;
5358 setLocked : function(colIndex, value, suppressEvent){
5359 if(this.isLocked(colIndex) == value){
5362 this.config[colIndex].locked = value;
5364 this.fireEvent("columnlockchange", this, colIndex, value);
5368 getTotalLockedWidth : function(){
5370 for(var i = 0; i < this.config.length; i++){
5371 if(this.isLocked(i) && !this.isHidden(i)){
5372 this.totalWidth += this.getColumnWidth(i);
5378 getLockedCount : function(){
5379 for(var i = 0, len = this.config.length; i < len; i++){
5380 if(!this.isLocked(i)){
5385 return this.config.length;
5389 * Returns the number of columns.
5392 getColumnCount : function(visibleOnly){
5393 if(visibleOnly === true){
5395 for(var i = 0, len = this.config.length; i < len; i++){
5396 if(!this.isHidden(i)){
5402 return this.config.length;
5406 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5407 * @param {Function} fn
5408 * @param {Object} scope (optional)
5409 * @return {Array} result
5411 getColumnsBy : function(fn, scope){
5413 for(var i = 0, len = this.config.length; i < len; i++){
5414 var c = this.config[i];
5415 if(fn.call(scope||this, c, i) === true){
5423 * Returns true if the specified column is sortable.
5424 * @param {Number} col The column index
5427 isSortable : function(col){
5428 if(typeof this.config[col].sortable == "undefined"){
5429 return this.defaultSortable;
5431 return this.config[col].sortable;
5435 * Returns the rendering (formatting) function defined for the column.
5436 * @param {Number} col The column index.
5437 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5439 getRenderer : function(col){
5440 if(!this.config[col].renderer){
5441 return Roo.grid.ColumnModel.defaultRenderer;
5443 return this.config[col].renderer;
5447 * Sets the rendering (formatting) function for a column.
5448 * @param {Number} col The column index
5449 * @param {Function} fn The function to use to process the cell's raw data
5450 * to return HTML markup for the grid view. The render function is called with
5451 * the following parameters:<ul>
5452 * <li>Data value.</li>
5453 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5454 * <li>css A CSS style string to apply to the table cell.</li>
5455 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5456 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5457 * <li>Row index</li>
5458 * <li>Column index</li>
5459 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5461 setRenderer : function(col, fn){
5462 this.config[col].renderer = fn;
5466 * Returns the width for the specified column.
5467 * @param {Number} col The column index
5470 getColumnWidth : function(col){
5471 return this.config[col].width * 1 || this.defaultWidth;
5475 * Sets the width for a column.
5476 * @param {Number} col The column index
5477 * @param {Number} width The new width
5479 setColumnWidth : function(col, width, suppressEvent){
5480 this.config[col].width = width;
5481 this.totalWidth = null;
5483 this.fireEvent("widthchange", this, col, width);
5488 * Returns the total width of all columns.
5489 * @param {Boolean} includeHidden True to include hidden column widths
5492 getTotalWidth : function(includeHidden){
5493 if(!this.totalWidth){
5494 this.totalWidth = 0;
5495 for(var i = 0, len = this.config.length; i < len; i++){
5496 if(includeHidden || !this.isHidden(i)){
5497 this.totalWidth += this.getColumnWidth(i);
5501 return this.totalWidth;
5505 * Returns the header for the specified column.
5506 * @param {Number} col The column index
5509 getColumnHeader : function(col){
5510 return this.config[col].header;
5514 * Sets the header for a column.
5515 * @param {Number} col The column index
5516 * @param {String} header The new header
5518 setColumnHeader : function(col, header){
5519 this.config[col].header = header;
5520 this.fireEvent("headerchange", this, col, header);
5524 * Returns the tooltip for the specified column.
5525 * @param {Number} col The column index
5528 getColumnTooltip : function(col){
5529 return this.config[col].tooltip;
5532 * Sets the tooltip for a column.
5533 * @param {Number} col The column index
5534 * @param {String} tooltip The new tooltip
5536 setColumnTooltip : function(col, tooltip){
5537 this.config[col].tooltip = tooltip;
5541 * Returns the dataIndex for the specified column.
5542 * @param {Number} col The column index
5545 getDataIndex : function(col){
5546 return this.config[col].dataIndex;
5550 * Sets the dataIndex for a column.
5551 * @param {Number} col The column index
5552 * @param {Number} dataIndex The new dataIndex
5554 setDataIndex : function(col, dataIndex){
5555 this.config[col].dataIndex = dataIndex;
5561 * Returns true if the cell is editable.
5562 * @param {Number} colIndex The column index
5563 * @param {Number} rowIndex The row index - this is nto actually used..?
5566 isCellEditable : function(colIndex, rowIndex){
5567 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5571 * Returns the editor defined for the cell/column.
5572 * return false or null to disable editing.
5573 * @param {Number} colIndex The column index
5574 * @param {Number} rowIndex The row index
5577 getCellEditor : function(colIndex, rowIndex){
5578 return this.config[colIndex].editor;
5582 * Sets if a column is editable.
5583 * @param {Number} col The column index
5584 * @param {Boolean} editable True if the column is editable
5586 setEditable : function(col, editable){
5587 this.config[col].editable = editable;
5592 * Returns true if the column is hidden.
5593 * @param {Number} colIndex The column index
5596 isHidden : function(colIndex){
5597 return this.config[colIndex].hidden;
5602 * Returns true if the column width cannot be changed
5604 isFixed : function(colIndex){
5605 return this.config[colIndex].fixed;
5609 * Returns true if the column can be resized
5612 isResizable : function(colIndex){
5613 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5616 * Sets if a column is hidden.
5617 * @param {Number} colIndex The column index
5618 * @param {Boolean} hidden True if the column is hidden
5620 setHidden : function(colIndex, hidden){
5621 this.config[colIndex].hidden = hidden;
5622 this.totalWidth = null;
5623 this.fireEvent("hiddenchange", this, colIndex, hidden);
5627 * Sets the editor for a column.
5628 * @param {Number} col The column index
5629 * @param {Object} editor The editor object
5631 setEditor : function(col, editor){
5632 this.config[col].editor = editor;
5636 Roo.grid.ColumnModel.defaultRenderer = function(value)
5638 if(typeof value == "object") {
5641 if(typeof value == "string" && value.length < 1){
5645 return String.format("{0}", value);
5648 // Alias for backwards compatibility
5649 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5652 * Ext JS Library 1.1.1
5653 * Copyright(c) 2006-2007, Ext JS, LLC.
5655 * Originally Released Under LGPL - original licence link has changed is not relivant.
5658 * <script type="text/javascript">
5662 * @class Roo.LoadMask
5663 * A simple utility class for generically masking elements while loading data. If the element being masked has
5664 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5665 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5666 * element's UpdateManager load indicator and will be destroyed after the initial load.
5668 * Create a new LoadMask
5669 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5670 * @param {Object} config The config object
5672 Roo.LoadMask = function(el, config){
5673 this.el = Roo.get(el);
5674 Roo.apply(this, config);
5676 this.store.on('beforeload', this.onBeforeLoad, this);
5677 this.store.on('load', this.onLoad, this);
5678 this.store.on('loadexception', this.onLoadException, this);
5679 this.removeMask = false;
5681 var um = this.el.getUpdateManager();
5682 um.showLoadIndicator = false; // disable the default indicator
5683 um.on('beforeupdate', this.onBeforeLoad, this);
5684 um.on('update', this.onLoad, this);
5685 um.on('failure', this.onLoad, this);
5686 this.removeMask = true;
5690 Roo.LoadMask.prototype = {
5692 * @cfg {Boolean} removeMask
5693 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5694 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5698 * The text to display in a centered loading message box (defaults to 'Loading...')
5702 * @cfg {String} msgCls
5703 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5705 msgCls : 'x-mask-loading',
5708 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5714 * Disables the mask to prevent it from being displayed
5716 disable : function(){
5717 this.disabled = true;
5721 * Enables the mask so that it can be displayed
5723 enable : function(){
5724 this.disabled = false;
5727 onLoadException : function()
5731 if (typeof(arguments[3]) != 'undefined') {
5732 Roo.MessageBox.alert("Error loading",arguments[3]);
5736 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5737 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5744 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5749 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5753 onBeforeLoad : function(){
5755 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5760 destroy : function(){
5762 this.store.un('beforeload', this.onBeforeLoad, this);
5763 this.store.un('load', this.onLoad, this);
5764 this.store.un('loadexception', this.onLoadException, this);
5766 var um = this.el.getUpdateManager();
5767 um.un('beforeupdate', this.onBeforeLoad, this);
5768 um.un('update', this.onLoad, this);
5769 um.un('failure', this.onLoad, this);
5780 * @class Roo.bootstrap.Table
5781 * @extends Roo.bootstrap.Component
5782 * Bootstrap Table class
5783 * @cfg {String} cls table class
5784 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5785 * @cfg {String} bgcolor Specifies the background color for a table
5786 * @cfg {Number} border Specifies whether the table cells should have borders or not
5787 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5788 * @cfg {Number} cellspacing Specifies the space between cells
5789 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5790 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5791 * @cfg {String} sortable Specifies that the table should be sortable
5792 * @cfg {String} summary Specifies a summary of the content of a table
5793 * @cfg {Number} width Specifies the width of a table
5794 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5796 * @cfg {boolean} striped Should the rows be alternative striped
5797 * @cfg {boolean} bordered Add borders to the table
5798 * @cfg {boolean} hover Add hover highlighting
5799 * @cfg {boolean} condensed Format condensed
5800 * @cfg {boolean} responsive Format condensed
5801 * @cfg {Boolean} loadMask (true|false) default false
5802 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5803 * @cfg {Boolean} headerShow (true|false) generate thead, default true
5804 * @cfg {Boolean} rowSelection (true|false) default false
5805 * @cfg {Boolean} cellSelection (true|false) default false
5806 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5807 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
5808 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
5812 * Create a new Table
5813 * @param {Object} config The config object
5816 Roo.bootstrap.Table = function(config){
5817 Roo.bootstrap.Table.superclass.constructor.call(this, config);
5822 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5823 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5824 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5825 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5827 this.sm = this.sm || {xtype: 'RowSelectionModel'};
5829 this.sm.grid = this;
5830 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5831 this.sm = this.selModel;
5832 this.sm.xmodule = this.xmodule || false;
5835 if (this.cm && typeof(this.cm.config) == 'undefined') {
5836 this.colModel = new Roo.grid.ColumnModel(this.cm);
5837 this.cm = this.colModel;
5838 this.cm.xmodule = this.xmodule || false;
5841 this.store= Roo.factory(this.store, Roo.data);
5842 this.ds = this.store;
5843 this.ds.xmodule = this.xmodule || false;
5846 if (this.footer && this.store) {
5847 this.footer.dataSource = this.ds;
5848 this.footer = Roo.factory(this.footer);
5855 * Fires when a cell is clicked
5856 * @param {Roo.bootstrap.Table} this
5857 * @param {Roo.Element} el
5858 * @param {Number} rowIndex
5859 * @param {Number} columnIndex
5860 * @param {Roo.EventObject} e
5864 * @event celldblclick
5865 * Fires when a cell is double clicked
5866 * @param {Roo.bootstrap.Table} this
5867 * @param {Roo.Element} el
5868 * @param {Number} rowIndex
5869 * @param {Number} columnIndex
5870 * @param {Roo.EventObject} e
5872 "celldblclick" : true,
5875 * Fires when a row is clicked
5876 * @param {Roo.bootstrap.Table} this
5877 * @param {Roo.Element} el
5878 * @param {Number} rowIndex
5879 * @param {Roo.EventObject} e
5883 * @event rowdblclick
5884 * Fires when a row is double clicked
5885 * @param {Roo.bootstrap.Table} this
5886 * @param {Roo.Element} el
5887 * @param {Number} rowIndex
5888 * @param {Roo.EventObject} e
5890 "rowdblclick" : true,
5893 * Fires when a mouseover occur
5894 * @param {Roo.bootstrap.Table} this
5895 * @param {Roo.Element} el
5896 * @param {Number} rowIndex
5897 * @param {Number} columnIndex
5898 * @param {Roo.EventObject} e
5903 * Fires when a mouseout occur
5904 * @param {Roo.bootstrap.Table} this
5905 * @param {Roo.Element} el
5906 * @param {Number} rowIndex
5907 * @param {Number} columnIndex
5908 * @param {Roo.EventObject} e
5913 * Fires when a row is rendered, so you can change add a style to it.
5914 * @param {Roo.bootstrap.Table} this
5915 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
5919 * @event rowsrendered
5920 * Fires when all the rows have been rendered
5921 * @param {Roo.bootstrap.Table} this
5923 'rowsrendered' : true,
5925 * @event contextmenu
5926 * The raw contextmenu event for the entire grid.
5927 * @param {Roo.EventObject} e
5929 "contextmenu" : true,
5931 * @event rowcontextmenu
5932 * Fires when a row is right clicked
5933 * @param {Roo.bootstrap.Table} this
5934 * @param {Number} rowIndex
5935 * @param {Roo.EventObject} e
5937 "rowcontextmenu" : true,
5939 * @event cellcontextmenu
5940 * Fires when a cell is right clicked
5941 * @param {Roo.bootstrap.Table} this
5942 * @param {Number} rowIndex
5943 * @param {Number} cellIndex
5944 * @param {Roo.EventObject} e
5946 "cellcontextmenu" : true,
5948 * @event headercontextmenu
5949 * Fires when a header is right clicked
5950 * @param {Roo.bootstrap.Table} this
5951 * @param {Number} columnIndex
5952 * @param {Roo.EventObject} e
5954 "headercontextmenu" : true
5958 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
5984 rowSelection : false,
5985 cellSelection : false,
5988 // Roo.Element - the tbody
5990 // Roo.Element - thead element
5993 container: false, // used by gridpanel...
5997 getAutoCreate : function()
5999 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6006 if (this.scrollBody) {
6007 cfg.cls += ' table-body-fixed';
6010 cfg.cls += ' table-striped';
6014 cfg.cls += ' table-hover';
6016 if (this.bordered) {
6017 cfg.cls += ' table-bordered';
6019 if (this.condensed) {
6020 cfg.cls += ' table-condensed';
6022 if (this.responsive) {
6023 cfg.cls += ' table-responsive';
6027 cfg.cls+= ' ' +this.cls;
6030 // this lot should be simplifed...
6033 cfg.align=this.align;
6036 cfg.bgcolor=this.bgcolor;
6039 cfg.border=this.border;
6041 if (this.cellpadding) {
6042 cfg.cellpadding=this.cellpadding;
6044 if (this.cellspacing) {
6045 cfg.cellspacing=this.cellspacing;
6048 cfg.frame=this.frame;
6051 cfg.rules=this.rules;
6053 if (this.sortable) {
6054 cfg.sortable=this.sortable;
6057 cfg.summary=this.summary;
6060 cfg.width=this.width;
6063 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6066 if(this.store || this.cm){
6067 if(this.headerShow){
6068 cfg.cn.push(this.renderHeader());
6071 cfg.cn.push(this.renderBody());
6073 if(this.footerShow){
6074 cfg.cn.push(this.renderFooter());
6076 // where does this come from?
6077 //cfg.cls+= ' TableGrid';
6080 return { cn : [ cfg ] };
6083 initEvents : function()
6085 if(!this.store || !this.cm){
6088 if (this.selModel) {
6089 this.selModel.initEvents();
6093 //Roo.log('initEvents with ds!!!!');
6095 this.mainBody = this.el.select('tbody', true).first();
6096 this.mainHead = this.el.select('thead', true).first();
6103 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6104 e.on('click', _this.sort, _this);
6107 this.mainBody.on("click", this.onClick, this);
6108 this.mainBody.on("dblclick", this.onDblClick, this);
6110 // why is this done????? = it breaks dialogs??
6111 //this.parent().el.setStyle('position', 'relative');
6115 this.footer.parentId = this.id;
6116 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6119 this.el.select('tfoot tr td').first().addClass('hide');
6123 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6125 this.store.on('load', this.onLoad, this);
6126 this.store.on('beforeload', this.onBeforeLoad, this);
6127 this.store.on('update', this.onUpdate, this);
6128 this.store.on('add', this.onAdd, this);
6129 this.store.on("clear", this.clear, this);
6131 this.el.on("contextmenu", this.onContextMenu, this);
6133 this.mainBody.on('scroll', this.onBodyScroll, this);
6138 onContextMenu : function(e, t)
6140 this.processEvent("contextmenu", e);
6143 processEvent : function(name, e)
6145 if (name != 'touchstart' ) {
6146 this.fireEvent(name, e);
6149 var t = e.getTarget();
6151 var cell = Roo.get(t);
6157 if(cell.findParent('tfoot', false, true)){
6161 if(cell.findParent('thead', false, true)){
6163 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6164 cell = Roo.get(t).findParent('th', false, true);
6166 Roo.log("failed to find th in thead?");
6167 Roo.log(e.getTarget());
6172 var cellIndex = cell.dom.cellIndex;
6174 var ename = name == 'touchstart' ? 'click' : name;
6175 this.fireEvent("header" + ename, this, cellIndex, e);
6180 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6181 cell = Roo.get(t).findParent('td', false, true);
6183 Roo.log("failed to find th in tbody?");
6184 Roo.log(e.getTarget());
6189 var row = cell.findParent('tr', false, true);
6190 var cellIndex = cell.dom.cellIndex;
6191 var rowIndex = row.dom.rowIndex - 1;
6195 this.fireEvent("row" + name, this, rowIndex, e);
6199 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6205 onMouseover : function(e, el)
6207 var cell = Roo.get(el);
6213 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6214 cell = cell.findParent('td', false, true);
6217 var row = cell.findParent('tr', false, true);
6218 var cellIndex = cell.dom.cellIndex;
6219 var rowIndex = row.dom.rowIndex - 1; // start from 0
6221 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6225 onMouseout : function(e, el)
6227 var cell = Roo.get(el);
6233 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6234 cell = cell.findParent('td', false, true);
6237 var row = cell.findParent('tr', false, true);
6238 var cellIndex = cell.dom.cellIndex;
6239 var rowIndex = row.dom.rowIndex - 1; // start from 0
6241 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6245 onClick : function(e, el)
6247 var cell = Roo.get(el);
6249 if(!cell || (!this.cellSelection && !this.rowSelection)){
6253 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6254 cell = cell.findParent('td', false, true);
6257 if(!cell || typeof(cell) == 'undefined'){
6261 var row = cell.findParent('tr', false, true);
6263 if(!row || typeof(row) == 'undefined'){
6267 var cellIndex = cell.dom.cellIndex;
6268 var rowIndex = this.getRowIndex(row);
6270 // why??? - should these not be based on SelectionModel?
6271 if(this.cellSelection){
6272 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6275 if(this.rowSelection){
6276 this.fireEvent('rowclick', this, row, rowIndex, e);
6282 onDblClick : function(e,el)
6284 var cell = Roo.get(el);
6286 if(!cell || (!this.cellSelection && !this.rowSelection)){
6290 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6291 cell = cell.findParent('td', false, true);
6294 if(!cell || typeof(cell) == 'undefined'){
6298 var row = cell.findParent('tr', false, true);
6300 if(!row || typeof(row) == 'undefined'){
6304 var cellIndex = cell.dom.cellIndex;
6305 var rowIndex = this.getRowIndex(row);
6307 if(this.cellSelection){
6308 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6311 if(this.rowSelection){
6312 this.fireEvent('rowdblclick', this, row, rowIndex, e);
6316 sort : function(e,el)
6318 var col = Roo.get(el);
6320 if(!col.hasClass('sortable')){
6324 var sort = col.attr('sort');
6327 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6331 this.store.sortInfo = {field : sort, direction : dir};
6334 Roo.log("calling footer first");
6335 this.footer.onClick('first');
6338 this.store.load({ params : { start : 0 } });
6342 renderHeader : function()
6350 this.totalWidth = 0;
6352 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6354 var config = cm.config[i];
6359 html: cm.getColumnHeader(i)
6364 if(typeof(config.sortable) != 'undefined' && config.sortable){
6366 c.html = '<i class="glyphicon"></i>' + c.html;
6369 if(typeof(config.lgHeader) != 'undefined'){
6370 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6373 if(typeof(config.mdHeader) != 'undefined'){
6374 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6377 if(typeof(config.smHeader) != 'undefined'){
6378 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6381 if(typeof(config.xsHeader) != 'undefined'){
6382 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6389 if(typeof(config.tooltip) != 'undefined'){
6390 c.tooltip = config.tooltip;
6393 if(typeof(config.colspan) != 'undefined'){
6394 c.colspan = config.colspan;
6397 if(typeof(config.hidden) != 'undefined' && config.hidden){
6398 c.style += ' display:none;';
6401 if(typeof(config.dataIndex) != 'undefined'){
6402 c.sort = config.dataIndex;
6407 if(typeof(config.align) != 'undefined' && config.align.length){
6408 c.style += ' text-align:' + config.align + ';';
6411 if(typeof(config.width) != 'undefined'){
6412 c.style += ' width:' + config.width + 'px;';
6413 this.totalWidth += config.width;
6415 this.totalWidth += 100; // assume minimum of 100 per column?
6418 if(typeof(config.cls) != 'undefined'){
6419 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6422 ['xs','sm','md','lg'].map(function(size){
6424 if(typeof(config[size]) == 'undefined'){
6428 if (!config[size]) { // 0 = hidden
6429 c.cls += ' hidden-' + size;
6433 c.cls += ' col-' + size + '-' + config[size];
6443 renderBody : function()
6453 colspan : this.cm.getColumnCount()
6463 renderFooter : function()
6473 colspan : this.cm.getColumnCount()
6487 // Roo.log('ds onload');
6492 var ds = this.store;
6494 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6495 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6496 if (_this.store.sortInfo) {
6498 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6499 e.select('i', true).addClass(['glyphicon-arrow-up']);
6502 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6503 e.select('i', true).addClass(['glyphicon-arrow-down']);
6508 var tbody = this.mainBody;
6510 if(ds.getCount() > 0){
6511 ds.data.each(function(d,rowIndex){
6512 var row = this.renderRow(cm, ds, rowIndex);
6514 tbody.createChild(row);
6518 if(row.cellObjects.length){
6519 Roo.each(row.cellObjects, function(r){
6520 _this.renderCellObject(r);
6527 Roo.each(this.el.select('tbody td', true).elements, function(e){
6528 e.on('mouseover', _this.onMouseover, _this);
6531 Roo.each(this.el.select('tbody td', true).elements, function(e){
6532 e.on('mouseout', _this.onMouseout, _this);
6534 this.fireEvent('rowsrendered', this);
6535 //if(this.loadMask){
6536 // this.maskEl.hide();
6543 onUpdate : function(ds,record)
6545 this.refreshRow(record);
6549 onRemove : function(ds, record, index, isUpdate){
6550 if(isUpdate !== true){
6551 this.fireEvent("beforerowremoved", this, index, record);
6553 var bt = this.mainBody.dom;
6555 var rows = this.el.select('tbody > tr', true).elements;
6557 if(typeof(rows[index]) != 'undefined'){
6558 bt.removeChild(rows[index].dom);
6561 // if(bt.rows[index]){
6562 // bt.removeChild(bt.rows[index]);
6565 if(isUpdate !== true){
6566 //this.stripeRows(index);
6567 //this.syncRowHeights(index, index);
6569 this.fireEvent("rowremoved", this, index, record);
6573 onAdd : function(ds, records, rowIndex)
6575 //Roo.log('on Add called');
6576 // - note this does not handle multiple adding very well..
6577 var bt = this.mainBody.dom;
6578 for (var i =0 ; i < records.length;i++) {
6579 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6580 //Roo.log(records[i]);
6581 //Roo.log(this.store.getAt(rowIndex+i));
6582 this.insertRow(this.store, rowIndex + i, false);
6589 refreshRow : function(record){
6590 var ds = this.store, index;
6591 if(typeof record == 'number'){
6593 record = ds.getAt(index);
6595 index = ds.indexOf(record);
6597 this.insertRow(ds, index, true);
6599 this.onRemove(ds, record, index+1, true);
6601 //this.syncRowHeights(index, index);
6603 this.fireEvent("rowupdated", this, index, record);
6606 insertRow : function(dm, rowIndex, isUpdate){
6609 this.fireEvent("beforerowsinserted", this, rowIndex);
6611 //var s = this.getScrollState();
6612 var row = this.renderRow(this.cm, this.store, rowIndex);
6613 // insert before rowIndex..
6614 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6618 if(row.cellObjects.length){
6619 Roo.each(row.cellObjects, function(r){
6620 _this.renderCellObject(r);
6625 this.fireEvent("rowsinserted", this, rowIndex);
6626 //this.syncRowHeights(firstRow, lastRow);
6627 //this.stripeRows(firstRow);
6634 getRowDom : function(rowIndex)
6636 var rows = this.el.select('tbody > tr', true).elements;
6638 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6641 // returns the object tree for a tr..
6644 renderRow : function(cm, ds, rowIndex)
6647 var d = ds.getAt(rowIndex);
6654 var cellObjects = [];
6656 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6657 var config = cm.config[i];
6659 var renderer = cm.getRenderer(i);
6663 if(typeof(renderer) !== 'undefined'){
6664 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6666 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6667 // and are rendered into the cells after the row is rendered - using the id for the element.
6669 if(typeof(value) === 'object'){
6679 rowIndex : rowIndex,
6684 this.fireEvent('rowclass', this, rowcfg);
6688 cls : rowcfg.rowClass,
6690 html: (typeof(value) === 'object') ? '' : value
6697 if(typeof(config.colspan) != 'undefined'){
6698 td.colspan = config.colspan;
6701 if(typeof(config.hidden) != 'undefined' && config.hidden){
6702 td.style += ' display:none;';
6705 if(typeof(config.align) != 'undefined' && config.align.length){
6706 td.style += ' text-align:' + config.align + ';';
6709 if(typeof(config.width) != 'undefined'){
6710 td.style += ' width:' + config.width + 'px;';
6713 if(typeof(config.cursor) != 'undefined'){
6714 td.style += ' cursor:' + config.cursor + ';';
6717 if(typeof(config.cls) != 'undefined'){
6718 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6721 ['xs','sm','md','lg'].map(function(size){
6723 if(typeof(config[size]) == 'undefined'){
6727 if (!config[size]) { // 0 = hidden
6728 td.cls += ' hidden-' + size;
6732 td.cls += ' col-' + size + '-' + config[size];
6740 row.cellObjects = cellObjects;
6748 onBeforeLoad : function()
6750 //Roo.log('ds onBeforeLoad');
6754 //if(this.loadMask){
6755 // this.maskEl.show();
6763 this.el.select('tbody', true).first().dom.innerHTML = '';
6766 * Show or hide a row.
6767 * @param {Number} rowIndex to show or hide
6768 * @param {Boolean} state hide
6770 setRowVisibility : function(rowIndex, state)
6772 var bt = this.mainBody.dom;
6774 var rows = this.el.select('tbody > tr', true).elements;
6776 if(typeof(rows[rowIndex]) == 'undefined'){
6779 rows[rowIndex].dom.style.display = state ? '' : 'none';
6783 getSelectionModel : function(){
6785 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6787 return this.selModel;
6790 * Render the Roo.bootstrap object from renderder
6792 renderCellObject : function(r)
6796 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6798 var t = r.cfg.render(r.container);
6801 Roo.each(r.cfg.cn, function(c){
6803 container: t.getChildContainer(),
6806 _this.renderCellObject(child);
6811 getRowIndex : function(row)
6815 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6826 * Returns the grid's underlying element = used by panel.Grid
6827 * @return {Element} The element
6829 getGridEl : function(){
6833 * Forces a resize - used by panel.Grid
6834 * @return {Element} The element
6836 autoSize : function()
6838 //var ctr = Roo.get(this.container.dom.parentElement);
6839 var ctr = Roo.get(this.el.dom);
6841 var thd = this.getGridEl().select('thead',true).first();
6842 var tbd = this.getGridEl().select('tbody', true).first();
6843 var tfd = this.getGridEl().select('tfoot', true).first();
6845 var cw = ctr.getWidth();
6849 tbd.setSize(ctr.getWidth(),
6850 ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
6852 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6855 cw = Math.max(cw, this.totalWidth);
6856 this.getGridEl().select('tr',true).setWidth(cw);
6857 // resize 'expandable coloumn?
6859 return; // we doe not have a view in this design..
6862 onBodyScroll: function()
6864 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6865 this.mainHead.setStyle({
6866 'position' : 'relative',
6867 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6872 var scrollHeight = this.mainBody.dom.scrollHeight;
6874 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
6876 var height = this.mainBody.getHeight();
6878 if(scrollHeight - height == scrollTop) {
6880 var total = this.ds.getTotalCount();
6882 if(this.footer.cursor + this.footer.pageSize < total){
6884 this.footer.ds.load({
6886 start : this.footer.cursor + this.footer.pageSize,
6887 limit : this.footer.pageSize
6908 * @class Roo.bootstrap.TableCell
6909 * @extends Roo.bootstrap.Component
6910 * Bootstrap TableCell class
6911 * @cfg {String} html cell contain text
6912 * @cfg {String} cls cell class
6913 * @cfg {String} tag cell tag (td|th) default td
6914 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6915 * @cfg {String} align Aligns the content in a cell
6916 * @cfg {String} axis Categorizes cells
6917 * @cfg {String} bgcolor Specifies the background color of a cell
6918 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6919 * @cfg {Number} colspan Specifies the number of columns a cell should span
6920 * @cfg {String} headers Specifies one or more header cells a cell is related to
6921 * @cfg {Number} height Sets the height of a cell
6922 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6923 * @cfg {Number} rowspan Sets the number of rows a cell should span
6924 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6925 * @cfg {String} valign Vertical aligns the content in a cell
6926 * @cfg {Number} width Specifies the width of a cell
6929 * Create a new TableCell
6930 * @param {Object} config The config object
6933 Roo.bootstrap.TableCell = function(config){
6934 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6937 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
6957 getAutoCreate : function(){
6958 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6978 cfg.align=this.align
6984 cfg.bgcolor=this.bgcolor
6987 cfg.charoff=this.charoff
6990 cfg.colspan=this.colspan
6993 cfg.headers=this.headers
6996 cfg.height=this.height
6999 cfg.nowrap=this.nowrap
7002 cfg.rowspan=this.rowspan
7005 cfg.scope=this.scope
7008 cfg.valign=this.valign
7011 cfg.width=this.width
7030 * @class Roo.bootstrap.TableRow
7031 * @extends Roo.bootstrap.Component
7032 * Bootstrap TableRow class
7033 * @cfg {String} cls row class
7034 * @cfg {String} align Aligns the content in a table row
7035 * @cfg {String} bgcolor Specifies a background color for a table row
7036 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7037 * @cfg {String} valign Vertical aligns the content in a table row
7040 * Create a new TableRow
7041 * @param {Object} config The config object
7044 Roo.bootstrap.TableRow = function(config){
7045 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7048 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
7056 getAutoCreate : function(){
7057 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7067 cfg.align = this.align;
7070 cfg.bgcolor = this.bgcolor;
7073 cfg.charoff = this.charoff;
7076 cfg.valign = this.valign;
7094 * @class Roo.bootstrap.TableBody
7095 * @extends Roo.bootstrap.Component
7096 * Bootstrap TableBody class
7097 * @cfg {String} cls element class
7098 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7099 * @cfg {String} align Aligns the content inside the element
7100 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7101 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7104 * Create a new TableBody
7105 * @param {Object} config The config object
7108 Roo.bootstrap.TableBody = function(config){
7109 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7112 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
7120 getAutoCreate : function(){
7121 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7135 cfg.align = this.align;
7138 cfg.charoff = this.charoff;
7141 cfg.valign = this.valign;
7148 // initEvents : function()
7155 // this.store = Roo.factory(this.store, Roo.data);
7156 // this.store.on('load', this.onLoad, this);
7158 // this.store.load();
7162 // onLoad: function ()
7164 // this.fireEvent('load', this);
7174 * Ext JS Library 1.1.1
7175 * Copyright(c) 2006-2007, Ext JS, LLC.
7177 * Originally Released Under LGPL - original licence link has changed is not relivant.
7180 * <script type="text/javascript">
7183 // as we use this in bootstrap.
7184 Roo.namespace('Roo.form');
7186 * @class Roo.form.Action
7187 * Internal Class used to handle form actions
7189 * @param {Roo.form.BasicForm} el The form element or its id
7190 * @param {Object} config Configuration options
7195 // define the action interface
7196 Roo.form.Action = function(form, options){
7198 this.options = options || {};
7201 * Client Validation Failed
7204 Roo.form.Action.CLIENT_INVALID = 'client';
7206 * Server Validation Failed
7209 Roo.form.Action.SERVER_INVALID = 'server';
7211 * Connect to Server Failed
7214 Roo.form.Action.CONNECT_FAILURE = 'connect';
7216 * Reading Data from Server Failed
7219 Roo.form.Action.LOAD_FAILURE = 'load';
7221 Roo.form.Action.prototype = {
7223 failureType : undefined,
7224 response : undefined,
7228 run : function(options){
7233 success : function(response){
7238 handleResponse : function(response){
7242 // default connection failure
7243 failure : function(response){
7245 this.response = response;
7246 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7247 this.form.afterAction(this, false);
7250 processResponse : function(response){
7251 this.response = response;
7252 if(!response.responseText){
7255 this.result = this.handleResponse(response);
7259 // utility functions used internally
7260 getUrl : function(appendParams){
7261 var url = this.options.url || this.form.url || this.form.el.dom.action;
7263 var p = this.getParams();
7265 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7271 getMethod : function(){
7272 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7275 getParams : function(){
7276 var bp = this.form.baseParams;
7277 var p = this.options.params;
7279 if(typeof p == "object"){
7280 p = Roo.urlEncode(Roo.applyIf(p, bp));
7281 }else if(typeof p == 'string' && bp){
7282 p += '&' + Roo.urlEncode(bp);
7285 p = Roo.urlEncode(bp);
7290 createCallback : function(){
7292 success: this.success,
7293 failure: this.failure,
7295 timeout: (this.form.timeout*1000),
7296 upload: this.form.fileUpload ? this.success : undefined
7301 Roo.form.Action.Submit = function(form, options){
7302 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7305 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7308 haveProgress : false,
7309 uploadComplete : false,
7311 // uploadProgress indicator.
7312 uploadProgress : function()
7314 if (!this.form.progressUrl) {
7318 if (!this.haveProgress) {
7319 Roo.MessageBox.progress("Uploading", "Uploading");
7321 if (this.uploadComplete) {
7322 Roo.MessageBox.hide();
7326 this.haveProgress = true;
7328 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7330 var c = new Roo.data.Connection();
7332 url : this.form.progressUrl,
7337 success : function(req){
7338 //console.log(data);
7342 rdata = Roo.decode(req.responseText)
7344 Roo.log("Invalid data from server..");
7348 if (!rdata || !rdata.success) {
7350 Roo.MessageBox.alert(Roo.encode(rdata));
7353 var data = rdata.data;
7355 if (this.uploadComplete) {
7356 Roo.MessageBox.hide();
7361 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7362 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7365 this.uploadProgress.defer(2000,this);
7368 failure: function(data) {
7369 Roo.log('progress url failed ');
7380 // run get Values on the form, so it syncs any secondary forms.
7381 this.form.getValues();
7383 var o = this.options;
7384 var method = this.getMethod();
7385 var isPost = method == 'POST';
7386 if(o.clientValidation === false || this.form.isValid()){
7388 if (this.form.progressUrl) {
7389 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7390 (new Date() * 1) + '' + Math.random());
7395 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7396 form:this.form.el.dom,
7397 url:this.getUrl(!isPost),
7399 params:isPost ? this.getParams() : null,
7400 isUpload: this.form.fileUpload
7403 this.uploadProgress();
7405 }else if (o.clientValidation !== false){ // client validation failed
7406 this.failureType = Roo.form.Action.CLIENT_INVALID;
7407 this.form.afterAction(this, false);
7411 success : function(response)
7413 this.uploadComplete= true;
7414 if (this.haveProgress) {
7415 Roo.MessageBox.hide();
7419 var result = this.processResponse(response);
7420 if(result === true || result.success){
7421 this.form.afterAction(this, true);
7425 this.form.markInvalid(result.errors);
7426 this.failureType = Roo.form.Action.SERVER_INVALID;
7428 this.form.afterAction(this, false);
7430 failure : function(response)
7432 this.uploadComplete= true;
7433 if (this.haveProgress) {
7434 Roo.MessageBox.hide();
7437 this.response = response;
7438 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7439 this.form.afterAction(this, false);
7442 handleResponse : function(response){
7443 if(this.form.errorReader){
7444 var rs = this.form.errorReader.read(response);
7447 for(var i = 0, len = rs.records.length; i < len; i++) {
7448 var r = rs.records[i];
7452 if(errors.length < 1){
7456 success : rs.success,
7462 ret = Roo.decode(response.responseText);
7466 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7476 Roo.form.Action.Load = function(form, options){
7477 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7478 this.reader = this.form.reader;
7481 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7486 Roo.Ajax.request(Roo.apply(
7487 this.createCallback(), {
7488 method:this.getMethod(),
7489 url:this.getUrl(false),
7490 params:this.getParams()
7494 success : function(response){
7496 var result = this.processResponse(response);
7497 if(result === true || !result.success || !result.data){
7498 this.failureType = Roo.form.Action.LOAD_FAILURE;
7499 this.form.afterAction(this, false);
7502 this.form.clearInvalid();
7503 this.form.setValues(result.data);
7504 this.form.afterAction(this, true);
7507 handleResponse : function(response){
7508 if(this.form.reader){
7509 var rs = this.form.reader.read(response);
7510 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7512 success : rs.success,
7516 return Roo.decode(response.responseText);
7520 Roo.form.Action.ACTION_TYPES = {
7521 'load' : Roo.form.Action.Load,
7522 'submit' : Roo.form.Action.Submit
7531 * @class Roo.bootstrap.Form
7532 * @extends Roo.bootstrap.Component
7533 * Bootstrap Form class
7534 * @cfg {String} method GET | POST (default POST)
7535 * @cfg {String} labelAlign top | left (default top)
7536 * @cfg {String} align left | right - for navbars
7537 * @cfg {Boolean} loadMask load mask when submit (default true)
7542 * @param {Object} config The config object
7546 Roo.bootstrap.Form = function(config){
7547 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7549 Roo.bootstrap.Form.popover.apply();
7553 * @event clientvalidation
7554 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7555 * @param {Form} this
7556 * @param {Boolean} valid true if the form has passed client-side validation
7558 clientvalidation: true,
7560 * @event beforeaction
7561 * Fires before any action is performed. Return false to cancel the action.
7562 * @param {Form} this
7563 * @param {Action} action The action to be performed
7567 * @event actionfailed
7568 * Fires when an action fails.
7569 * @param {Form} this
7570 * @param {Action} action The action that failed
7572 actionfailed : true,
7574 * @event actioncomplete
7575 * Fires when an action is completed.
7576 * @param {Form} this
7577 * @param {Action} action The action that completed
7579 actioncomplete : true
7584 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7587 * @cfg {String} method
7588 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7593 * The URL to use for form actions if one isn't supplied in the action options.
7596 * @cfg {Boolean} fileUpload
7597 * Set to true if this form is a file upload.
7601 * @cfg {Object} baseParams
7602 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7606 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7610 * @cfg {Sting} align (left|right) for navbar forms
7615 activeAction : null,
7618 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7619 * element by passing it or its id or mask the form itself by passing in true.
7622 waitMsgTarget : false,
7627 * @cfg {Boolean} errorMask (true|false) default false
7632 * @cfg {Number} maskOffset Default 100
7637 * @cfg {Boolean} maskBody
7641 getAutoCreate : function(){
7645 method : this.method || 'POST',
7646 id : this.id || Roo.id(),
7649 if (this.parent().xtype.match(/^Nav/)) {
7650 cfg.cls = 'navbar-form navbar-' + this.align;
7654 if (this.labelAlign == 'left' ) {
7655 cfg.cls += ' form-horizontal';
7661 initEvents : function()
7663 this.el.on('submit', this.onSubmit, this);
7664 // this was added as random key presses on the form where triggering form submit.
7665 this.el.on('keypress', function(e) {
7666 if (e.getCharCode() != 13) {
7669 // we might need to allow it for textareas.. and some other items.
7670 // check e.getTarget().
7672 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7676 Roo.log("keypress blocked");
7684 onSubmit : function(e){
7689 * Returns true if client-side validation on the form is successful.
7692 isValid : function(){
7693 var items = this.getItems();
7697 items.each(function(f){
7703 if(!target && f.el.isVisible(true)){
7709 if(this.errorMask && !valid){
7710 Roo.bootstrap.Form.popover.mask(this, target);
7717 * Returns true if any fields in this form have changed since their original load.
7720 isDirty : function(){
7722 var items = this.getItems();
7723 items.each(function(f){
7733 * Performs a predefined action (submit or load) or custom actions you define on this form.
7734 * @param {String} actionName The name of the action type
7735 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
7736 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7737 * accept other config options):
7739 Property Type Description
7740 ---------------- --------------- ----------------------------------------------------------------------------------
7741 url String The url for the action (defaults to the form's url)
7742 method String The form method to use (defaults to the form's method, or POST if not defined)
7743 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
7744 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
7745 validate the form on the client (defaults to false)
7747 * @return {BasicForm} this
7749 doAction : function(action, options){
7750 if(typeof action == 'string'){
7751 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7753 if(this.fireEvent('beforeaction', this, action) !== false){
7754 this.beforeAction(action);
7755 action.run.defer(100, action);
7761 beforeAction : function(action){
7762 var o = action.options;
7767 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
7769 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7772 // not really supported yet.. ??
7774 //if(this.waitMsgTarget === true){
7775 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7776 //}else if(this.waitMsgTarget){
7777 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7778 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7780 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7786 afterAction : function(action, success){
7787 this.activeAction = null;
7788 var o = action.options;
7793 Roo.get(document.body).unmask();
7799 //if(this.waitMsgTarget === true){
7800 // this.el.unmask();
7801 //}else if(this.waitMsgTarget){
7802 // this.waitMsgTarget.unmask();
7804 // Roo.MessageBox.updateProgress(1);
7805 // Roo.MessageBox.hide();
7812 Roo.callback(o.success, o.scope, [this, action]);
7813 this.fireEvent('actioncomplete', this, action);
7817 // failure condition..
7818 // we have a scenario where updates need confirming.
7819 // eg. if a locking scenario exists..
7820 // we look for { errors : { needs_confirm : true }} in the response.
7822 (typeof(action.result) != 'undefined') &&
7823 (typeof(action.result.errors) != 'undefined') &&
7824 (typeof(action.result.errors.needs_confirm) != 'undefined')
7827 Roo.log("not supported yet");
7830 Roo.MessageBox.confirm(
7831 "Change requires confirmation",
7832 action.result.errorMsg,
7837 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
7847 Roo.callback(o.failure, o.scope, [this, action]);
7848 // show an error message if no failed handler is set..
7849 if (!this.hasListener('actionfailed')) {
7850 Roo.log("need to add dialog support");
7852 Roo.MessageBox.alert("Error",
7853 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7854 action.result.errorMsg :
7855 "Saving Failed, please check your entries or try again"
7860 this.fireEvent('actionfailed', this, action);
7865 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7866 * @param {String} id The value to search for
7869 findField : function(id){
7870 var items = this.getItems();
7871 var field = items.get(id);
7873 items.each(function(f){
7874 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7881 return field || null;
7884 * Mark fields in this form invalid in bulk.
7885 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7886 * @return {BasicForm} this
7888 markInvalid : function(errors){
7889 if(errors instanceof Array){
7890 for(var i = 0, len = errors.length; i < len; i++){
7891 var fieldError = errors[i];
7892 var f = this.findField(fieldError.id);
7894 f.markInvalid(fieldError.msg);
7900 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7901 field.markInvalid(errors[id]);
7905 //Roo.each(this.childForms || [], function (f) {
7906 // f.markInvalid(errors);
7913 * Set values for fields in this form in bulk.
7914 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7915 * @return {BasicForm} this
7917 setValues : function(values){
7918 if(values instanceof Array){ // array of objects
7919 for(var i = 0, len = values.length; i < len; i++){
7921 var f = this.findField(v.id);
7923 f.setValue(v.value);
7924 if(this.trackResetOnLoad){
7925 f.originalValue = f.getValue();
7929 }else{ // object hash
7932 if(typeof values[id] != 'function' && (field = this.findField(id))){
7934 if (field.setFromData &&
7936 field.displayField &&
7937 // combos' with local stores can
7938 // be queried via setValue()
7939 // to set their value..
7940 (field.store && !field.store.isLocal)
7944 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7945 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7946 field.setFromData(sd);
7948 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
7950 field.setFromData(values);
7953 field.setValue(values[id]);
7957 if(this.trackResetOnLoad){
7958 field.originalValue = field.getValue();
7964 //Roo.each(this.childForms || [], function (f) {
7965 // f.setValues(values);
7972 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7973 * they are returned as an array.
7974 * @param {Boolean} asString
7977 getValues : function(asString){
7978 //if (this.childForms) {
7979 // copy values from the child forms
7980 // Roo.each(this.childForms, function (f) {
7981 // this.setValues(f.getValues());
7987 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7988 if(asString === true){
7991 return Roo.urlDecode(fs);
7995 * Returns the fields in this form as an object with key/value pairs.
7996 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7999 getFieldValues : function(with_hidden)
8001 var items = this.getItems();
8003 items.each(function(f){
8009 var v = f.getValue();
8011 if (f.inputType =='radio') {
8012 if (typeof(ret[f.getName()]) == 'undefined') {
8013 ret[f.getName()] = ''; // empty..
8016 if (!f.el.dom.checked) {
8024 if(f.xtype == 'MoneyField'){
8025 ret[f.currencyName] = f.getCurrency();
8028 // not sure if this supported any more..
8029 if ((typeof(v) == 'object') && f.getRawValue) {
8030 v = f.getRawValue() ; // dates..
8032 // combo boxes where name != hiddenName...
8033 if (f.name !== false && f.name != '' && f.name != f.getName()) {
8034 ret[f.name] = f.getRawValue();
8036 ret[f.getName()] = v;
8043 * Clears all invalid messages in this form.
8044 * @return {BasicForm} this
8046 clearInvalid : function(){
8047 var items = this.getItems();
8049 items.each(function(f){
8060 * @return {BasicForm} this
8063 var items = this.getItems();
8064 items.each(function(f){
8068 Roo.each(this.childForms || [], function (f) {
8076 getItems : function()
8078 var r=new Roo.util.MixedCollection(false, function(o){
8079 return o.id || (o.id = Roo.id());
8081 var iter = function(el) {
8088 Roo.each(el.items,function(e) {
8102 Roo.apply(Roo.bootstrap.Form, {
8129 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8130 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8131 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8132 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8135 this.maskEl.top.enableDisplayMode("block");
8136 this.maskEl.left.enableDisplayMode("block");
8137 this.maskEl.bottom.enableDisplayMode("block");
8138 this.maskEl.right.enableDisplayMode("block");
8140 this.toolTip = new Roo.bootstrap.Tooltip({
8141 cls : 'roo-form-error-popover',
8143 'left' : ['r-l', [-2,0], 'right'],
8144 'right' : ['l-r', [2,0], 'left'],
8145 'bottom' : ['tl-bl', [0,2], 'top'],
8146 'top' : [ 'bl-tl', [0,-2], 'bottom']
8150 this.toolTip.render(Roo.get(document.body));
8152 this.toolTip.el.enableDisplayMode("block");
8154 Roo.get(document.body).on('click', function(){
8158 Roo.get(document.body).on('touchstart', function(){
8162 this.isApplied = true
8165 mask : function(form, target)
8169 this.target = target;
8171 if(!this.form.errorMask || !target.el){
8175 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8177 Roo.log(scrollable);
8179 var ot = this.target.el.calcOffsetsTo(scrollable);
8181 var scrollTo = ot[1] - this.form.maskOffset;
8183 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8185 scrollable.scrollTo('top', scrollTo);
8187 var box = this.target.el.getBox();
8189 var zIndex = Roo.bootstrap.Modal.zIndex++;
8192 this.maskEl.top.setStyle('position', 'absolute');
8193 this.maskEl.top.setStyle('z-index', zIndex);
8194 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8195 this.maskEl.top.setLeft(0);
8196 this.maskEl.top.setTop(0);
8197 this.maskEl.top.show();
8199 this.maskEl.left.setStyle('position', 'absolute');
8200 this.maskEl.left.setStyle('z-index', zIndex);
8201 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8202 this.maskEl.left.setLeft(0);
8203 this.maskEl.left.setTop(box.y - this.padding);
8204 this.maskEl.left.show();
8206 this.maskEl.bottom.setStyle('position', 'absolute');
8207 this.maskEl.bottom.setStyle('z-index', zIndex);
8208 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8209 this.maskEl.bottom.setLeft(0);
8210 this.maskEl.bottom.setTop(box.bottom + this.padding);
8211 this.maskEl.bottom.show();
8213 this.maskEl.right.setStyle('position', 'absolute');
8214 this.maskEl.right.setStyle('z-index', zIndex);
8215 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8216 this.maskEl.right.setLeft(box.right + this.padding);
8217 this.maskEl.right.setTop(box.y - this.padding);
8218 this.maskEl.right.show();
8220 this.toolTip.bindEl = this.target.el;
8222 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8224 var tip = this.target.blankText;
8226 if(this.target.getValue() !== '' ) {
8228 if (this.target.invalidText.length) {
8229 tip = this.target.invalidText;
8230 } else if (this.target.regexText.length){
8231 tip = this.target.regexText;
8235 this.toolTip.show(tip);
8237 this.intervalID = window.setInterval(function() {
8238 Roo.bootstrap.Form.popover.unmask();
8241 window.onwheel = function(){ return false;};
8243 (function(){ this.isMasked = true; }).defer(500, this);
8249 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8253 this.maskEl.top.setStyle('position', 'absolute');
8254 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8255 this.maskEl.top.hide();
8257 this.maskEl.left.setStyle('position', 'absolute');
8258 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8259 this.maskEl.left.hide();
8261 this.maskEl.bottom.setStyle('position', 'absolute');
8262 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8263 this.maskEl.bottom.hide();
8265 this.maskEl.right.setStyle('position', 'absolute');
8266 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8267 this.maskEl.right.hide();
8269 this.toolTip.hide();
8271 this.toolTip.el.hide();
8273 window.onwheel = function(){ return true;};
8275 if(this.intervalID){
8276 window.clearInterval(this.intervalID);
8277 this.intervalID = false;
8280 this.isMasked = false;
8290 * Ext JS Library 1.1.1
8291 * Copyright(c) 2006-2007, Ext JS, LLC.
8293 * Originally Released Under LGPL - original licence link has changed is not relivant.
8296 * <script type="text/javascript">
8299 * @class Roo.form.VTypes
8300 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8303 Roo.form.VTypes = function(){
8304 // closure these in so they are only created once.
8305 var alpha = /^[a-zA-Z_]+$/;
8306 var alphanum = /^[a-zA-Z0-9_]+$/;
8307 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8308 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8310 // All these messages and functions are configurable
8313 * The function used to validate email addresses
8314 * @param {String} value The email address
8316 'email' : function(v){
8317 return email.test(v);
8320 * The error text to display when the email validation function returns false
8323 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8325 * The keystroke filter mask to be applied on email input
8328 'emailMask' : /[a-z0-9_\.\-@]/i,
8331 * The function used to validate URLs
8332 * @param {String} value The URL
8334 'url' : function(v){
8338 * The error text to display when the url validation function returns false
8341 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8344 * The function used to validate alpha values
8345 * @param {String} value The value
8347 'alpha' : function(v){
8348 return alpha.test(v);
8351 * The error text to display when the alpha validation function returns false
8354 'alphaText' : 'This field should only contain letters and _',
8356 * The keystroke filter mask to be applied on alpha input
8359 'alphaMask' : /[a-z_]/i,
8362 * The function used to validate alphanumeric values
8363 * @param {String} value The value
8365 'alphanum' : function(v){
8366 return alphanum.test(v);
8369 * The error text to display when the alphanumeric validation function returns false
8372 'alphanumText' : 'This field should only contain letters, numbers and _',
8374 * The keystroke filter mask to be applied on alphanumeric input
8377 'alphanumMask' : /[a-z0-9_]/i
8387 * @class Roo.bootstrap.Input
8388 * @extends Roo.bootstrap.Component
8389 * Bootstrap Input class
8390 * @cfg {Boolean} disabled is it disabled
8391 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8392 * @cfg {String} name name of the input
8393 * @cfg {string} fieldLabel - the label associated
8394 * @cfg {string} placeholder - placeholder to put in text.
8395 * @cfg {string} before - input group add on before
8396 * @cfg {string} after - input group add on after
8397 * @cfg {string} size - (lg|sm) or leave empty..
8398 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8399 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8400 * @cfg {Number} md colspan out of 12 for computer-sized screens
8401 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8402 * @cfg {string} value default value of the input
8403 * @cfg {Number} labelWidth set the width of label
8404 * @cfg {Number} labellg set the width of label (1-12)
8405 * @cfg {Number} labelmd set the width of label (1-12)
8406 * @cfg {Number} labelsm set the width of label (1-12)
8407 * @cfg {Number} labelxs set the width of label (1-12)
8408 * @cfg {String} labelAlign (top|left)
8409 * @cfg {Boolean} readOnly Specifies that the field should be read-only
8410 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8411 * @cfg {String} indicatorpos (left|right) default left
8413 * @cfg {String} align (left|center|right) Default left
8414 * @cfg {Boolean} forceFeedback (true|false) Default false
8420 * Create a new Input
8421 * @param {Object} config The config object
8424 Roo.bootstrap.Input = function(config){
8426 Roo.bootstrap.Input.superclass.constructor.call(this, config);
8431 * Fires when this field receives input focus.
8432 * @param {Roo.form.Field} this
8437 * Fires when this field loses input focus.
8438 * @param {Roo.form.Field} this
8443 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8444 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8445 * @param {Roo.form.Field} this
8446 * @param {Roo.EventObject} e The event object
8451 * Fires just before the field blurs if the field value has changed.
8452 * @param {Roo.form.Field} this
8453 * @param {Mixed} newValue The new value
8454 * @param {Mixed} oldValue The original value
8459 * Fires after the field has been marked as invalid.
8460 * @param {Roo.form.Field} this
8461 * @param {String} msg The validation message
8466 * Fires after the field has been validated with no errors.
8467 * @param {Roo.form.Field} this
8472 * Fires after the key up
8473 * @param {Roo.form.Field} this
8474 * @param {Roo.EventObject} e The event Object
8480 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
8482 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8483 automatic validation (defaults to "keyup").
8485 validationEvent : "keyup",
8487 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8489 validateOnBlur : true,
8491 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8493 validationDelay : 250,
8495 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8497 focusClass : "x-form-focus", // not needed???
8501 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8503 invalidClass : "has-warning",
8506 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8508 validClass : "has-success",
8511 * @cfg {Boolean} hasFeedback (true|false) default true
8516 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8518 invalidFeedbackClass : "glyphicon-warning-sign",
8521 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8523 validFeedbackClass : "glyphicon-ok",
8526 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8528 selectOnFocus : false,
8531 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8535 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8540 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8542 disableKeyFilter : false,
8545 * @cfg {Boolean} disabled True to disable the field (defaults to false).
8549 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8553 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8555 blankText : "Please complete this mandatory field",
8558 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8562 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8564 maxLength : Number.MAX_VALUE,
8566 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8568 minLengthText : "The minimum length for this field is {0}",
8570 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8572 maxLengthText : "The maximum length for this field is {0}",
8576 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8577 * If available, this function will be called only after the basic validators all return true, and will be passed the
8578 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8582 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8583 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8584 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
8588 * @cfg {String} regexText -- Depricated - use Invalid Text
8593 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8599 autocomplete: false,
8618 formatedValue : false,
8619 forceFeedback : false,
8621 indicatorpos : 'left',
8628 parentLabelAlign : function()
8631 while (parent.parent()) {
8632 parent = parent.parent();
8633 if (typeof(parent.labelAlign) !='undefined') {
8634 return parent.labelAlign;
8641 getAutoCreate : function()
8643 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8649 if(this.inputType != 'hidden'){
8650 cfg.cls = 'form-group' //input-group
8656 type : this.inputType,
8658 cls : 'form-control',
8659 placeholder : this.placeholder || '',
8660 autocomplete : this.autocomplete || 'new-password'
8664 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8667 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8668 input.maxLength = this.maxLength;
8671 if (this.disabled) {
8672 input.disabled=true;
8675 if (this.readOnly) {
8676 input.readonly=true;
8680 input.name = this.name;
8684 input.cls += ' input-' + this.size;
8688 ['xs','sm','md','lg'].map(function(size){
8689 if (settings[size]) {
8690 cfg.cls += ' col-' + size + '-' + settings[size];
8694 var inputblock = input;
8698 cls: 'glyphicon form-control-feedback'
8701 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8704 cls : 'has-feedback',
8712 if (this.before || this.after) {
8715 cls : 'input-group',
8719 if (this.before && typeof(this.before) == 'string') {
8721 inputblock.cn.push({
8723 cls : 'roo-input-before input-group-addon',
8727 if (this.before && typeof(this.before) == 'object') {
8728 this.before = Roo.factory(this.before);
8730 inputblock.cn.push({
8732 cls : 'roo-input-before input-group-' +
8733 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8737 inputblock.cn.push(input);
8739 if (this.after && typeof(this.after) == 'string') {
8740 inputblock.cn.push({
8742 cls : 'roo-input-after input-group-addon',
8746 if (this.after && typeof(this.after) == 'object') {
8747 this.after = Roo.factory(this.after);
8749 inputblock.cn.push({
8751 cls : 'roo-input-after input-group-' +
8752 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8756 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8757 inputblock.cls += ' has-feedback';
8758 inputblock.cn.push(feedback);
8762 if (align ==='left' && this.fieldLabel.length) {
8764 cfg.cls += ' roo-form-group-label-left';
8769 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8770 tooltip : 'This field is required'
8775 cls : 'control-label',
8776 html : this.fieldLabel
8787 var labelCfg = cfg.cn[1];
8788 var contentCfg = cfg.cn[2];
8790 if(this.indicatorpos == 'right'){
8795 cls : 'control-label',
8799 html : this.fieldLabel
8803 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8804 tooltip : 'This field is required'
8817 labelCfg = cfg.cn[0];
8818 contentCfg = cfg.cn[1];
8822 if(this.labelWidth > 12){
8823 labelCfg.style = "width: " + this.labelWidth + 'px';
8826 if(this.labelWidth < 13 && this.labelmd == 0){
8827 this.labelmd = this.labelWidth;
8830 if(this.labellg > 0){
8831 labelCfg.cls += ' col-lg-' + this.labellg;
8832 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
8835 if(this.labelmd > 0){
8836 labelCfg.cls += ' col-md-' + this.labelmd;
8837 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
8840 if(this.labelsm > 0){
8841 labelCfg.cls += ' col-sm-' + this.labelsm;
8842 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
8845 if(this.labelxs > 0){
8846 labelCfg.cls += ' col-xs-' + this.labelxs;
8847 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
8851 } else if ( this.fieldLabel.length) {
8856 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8857 tooltip : 'This field is required'
8861 //cls : 'input-group-addon',
8862 html : this.fieldLabel
8870 if(this.indicatorpos == 'right'){
8875 //cls : 'input-group-addon',
8876 html : this.fieldLabel
8881 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8882 tooltip : 'This field is required'
8902 if (this.parentType === 'Navbar' && this.parent().bar) {
8903 cfg.cls += ' navbar-form';
8906 if (this.parentType === 'NavGroup') {
8907 cfg.cls += ' navbar-form';
8915 * return the real input element.
8917 inputEl: function ()
8919 return this.el.select('input.form-control',true).first();
8922 tooltipEl : function()
8924 return this.inputEl();
8927 indicatorEl : function()
8929 var indicator = this.el.select('i.roo-required-indicator',true).first();
8939 setDisabled : function(v)
8941 var i = this.inputEl().dom;
8943 i.removeAttribute('disabled');
8947 i.setAttribute('disabled','true');
8949 initEvents : function()
8952 this.inputEl().on("keydown" , this.fireKey, this);
8953 this.inputEl().on("focus", this.onFocus, this);
8954 this.inputEl().on("blur", this.onBlur, this);
8956 this.inputEl().relayEvent('keyup', this);
8958 this.indicator = this.indicatorEl();
8961 this.indicator.addClass('invisible');
8965 // reference to original value for reset
8966 this.originalValue = this.getValue();
8967 //Roo.form.TextField.superclass.initEvents.call(this);
8968 if(this.validationEvent == 'keyup'){
8969 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8970 this.inputEl().on('keyup', this.filterValidation, this);
8972 else if(this.validationEvent !== false){
8973 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8976 if(this.selectOnFocus){
8977 this.on("focus", this.preFocus, this);
8980 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8981 this.inputEl().on("keypress", this.filterKeys, this);
8983 this.inputEl().relayEvent('keypress', this);
8986 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
8987 this.el.on("click", this.autoSize, this);
8990 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8991 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8994 if (typeof(this.before) == 'object') {
8995 this.before.render(this.el.select('.roo-input-before',true).first());
8997 if (typeof(this.after) == 'object') {
8998 this.after.render(this.el.select('.roo-input-after',true).first());
9003 filterValidation : function(e){
9004 if(!e.isNavKeyPress()){
9005 this.validationTask.delay(this.validationDelay);
9009 * Validates the field value
9010 * @return {Boolean} True if the value is valid, else false
9012 validate : function(){
9013 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9014 if(this.disabled || this.validateValue(this.getRawValue())){
9025 * Validates a value according to the field's validation rules and marks the field as invalid
9026 * if the validation fails
9027 * @param {Mixed} value The value to validate
9028 * @return {Boolean} True if the value is valid, else false
9030 validateValue : function(value){
9031 if(value.length < 1) { // if it's blank
9032 if(this.allowBlank){
9035 return this.inputEl().hasClass('hide') ? true : false;
9038 if(value.length < this.minLength){
9041 if(value.length > this.maxLength){
9045 var vt = Roo.form.VTypes;
9046 if(!vt[this.vtype](value, this)){
9050 if(typeof this.validator == "function"){
9051 var msg = this.validator(value);
9055 if (typeof(msg) == 'string') {
9056 this.invalidText = msg;
9060 if(this.regex && !this.regex.test(value)){
9070 fireKey : function(e){
9071 //Roo.log('field ' + e.getKey());
9072 if(e.isNavKeyPress()){
9073 this.fireEvent("specialkey", this, e);
9076 focus : function (selectText){
9078 this.inputEl().focus();
9079 if(selectText === true){
9080 this.inputEl().dom.select();
9086 onFocus : function(){
9087 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9088 // this.el.addClass(this.focusClass);
9091 this.hasFocus = true;
9092 this.startValue = this.getValue();
9093 this.fireEvent("focus", this);
9097 beforeBlur : Roo.emptyFn,
9101 onBlur : function(){
9103 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9104 //this.el.removeClass(this.focusClass);
9106 this.hasFocus = false;
9107 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9110 var v = this.getValue();
9111 if(String(v) !== String(this.startValue)){
9112 this.fireEvent('change', this, v, this.startValue);
9114 this.fireEvent("blur", this);
9118 * Resets the current field value to the originally loaded value and clears any validation messages
9121 this.setValue(this.originalValue);
9125 * Returns the name of the field
9126 * @return {Mixed} name The name field
9128 getName: function(){
9132 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
9133 * @return {Mixed} value The field value
9135 getValue : function(){
9137 var v = this.inputEl().getValue();
9142 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
9143 * @return {Mixed} value The field value
9145 getRawValue : function(){
9146 var v = this.inputEl().getValue();
9152 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
9153 * @param {Mixed} value The value to set
9155 setRawValue : function(v){
9156 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9159 selectText : function(start, end){
9160 var v = this.getRawValue();
9162 start = start === undefined ? 0 : start;
9163 end = end === undefined ? v.length : end;
9164 var d = this.inputEl().dom;
9165 if(d.setSelectionRange){
9166 d.setSelectionRange(start, end);
9167 }else if(d.createTextRange){
9168 var range = d.createTextRange();
9169 range.moveStart("character", start);
9170 range.moveEnd("character", v.length-end);
9177 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
9178 * @param {Mixed} value The value to set
9180 setValue : function(v){
9183 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9189 processValue : function(value){
9190 if(this.stripCharsRe){
9191 var newValue = value.replace(this.stripCharsRe, '');
9192 if(newValue !== value){
9193 this.setRawValue(newValue);
9200 preFocus : function(){
9202 if(this.selectOnFocus){
9203 this.inputEl().dom.select();
9206 filterKeys : function(e){
9208 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9211 var c = e.getCharCode(), cc = String.fromCharCode(c);
9212 if(Roo.isIE && (e.isSpecialKey() || !cc)){
9215 if(!this.maskRe.test(cc)){
9220 * Clear any invalid styles/messages for this field
9222 clearInvalid : function(){
9224 if(!this.el || this.preventMark){ // not rendered
9229 this.el.removeClass(this.invalidClass);
9231 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9233 var feedback = this.el.select('.form-control-feedback', true).first();
9236 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9241 this.fireEvent('valid', this);
9245 * Mark this field as valid
9247 markValid : function()
9249 if(!this.el || this.preventMark){ // not rendered...
9253 this.el.removeClass([this.invalidClass, this.validClass]);
9255 var feedback = this.el.select('.form-control-feedback', true).first();
9258 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9265 if(this.allowBlank && !this.getRawValue().length){
9270 this.indicator.removeClass('visible');
9271 this.indicator.addClass('invisible');
9274 this.el.addClass(this.validClass);
9276 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9278 var feedback = this.el.select('.form-control-feedback', true).first();
9281 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9282 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9287 this.fireEvent('valid', this);
9291 * Mark this field as invalid
9292 * @param {String} msg The validation message
9294 markInvalid : function(msg)
9296 if(!this.el || this.preventMark){ // not rendered
9300 this.el.removeClass([this.invalidClass, this.validClass]);
9302 var feedback = this.el.select('.form-control-feedback', true).first();
9305 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9312 if(this.allowBlank && !this.getRawValue().length){
9317 this.indicator.removeClass('invisible');
9318 this.indicator.addClass('visible');
9321 this.el.addClass(this.invalidClass);
9323 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9325 var feedback = this.el.select('.form-control-feedback', true).first();
9328 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9330 if(this.getValue().length || this.forceFeedback){
9331 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9338 this.fireEvent('invalid', this, msg);
9341 SafariOnKeyDown : function(event)
9343 // this is a workaround for a password hang bug on chrome/ webkit.
9344 if (this.inputEl().dom.type != 'password') {
9348 var isSelectAll = false;
9350 if(this.inputEl().dom.selectionEnd > 0){
9351 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9353 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9354 event.preventDefault();
9359 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9361 event.preventDefault();
9362 // this is very hacky as keydown always get's upper case.
9364 var cc = String.fromCharCode(event.getCharCode());
9365 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
9369 adjustWidth : function(tag, w){
9370 tag = tag.toLowerCase();
9371 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9372 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9376 if(tag == 'textarea'){
9379 }else if(Roo.isOpera){
9383 if(tag == 'textarea'){
9391 setFieldLabel : function(v)
9398 var ar = this.el.select('label > span',true);
9400 if (ar.elements.length) {
9401 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9402 this.fieldLabel = v;
9406 var br = this.el.select('label',true);
9408 if(br.elements.length) {
9409 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9410 this.fieldLabel = v;
9414 Roo.log('Cannot Found any of label > span || label in input');
9418 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9419 this.fieldLabel = v;
9434 * @class Roo.bootstrap.TextArea
9435 * @extends Roo.bootstrap.Input
9436 * Bootstrap TextArea class
9437 * @cfg {Number} cols Specifies the visible width of a text area
9438 * @cfg {Number} rows Specifies the visible number of lines in a text area
9439 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9440 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9441 * @cfg {string} html text
9444 * Create a new TextArea
9445 * @param {Object} config The config object
9448 Roo.bootstrap.TextArea = function(config){
9449 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9453 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
9463 getAutoCreate : function(){
9465 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9471 if(this.inputType != 'hidden'){
9472 cfg.cls = 'form-group' //input-group
9480 value : this.value || '',
9481 html: this.html || '',
9482 cls : 'form-control',
9483 placeholder : this.placeholder || ''
9487 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9488 input.maxLength = this.maxLength;
9492 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9496 input.cols = this.cols;
9499 if (this.readOnly) {
9500 input.readonly = true;
9504 input.name = this.name;
9508 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9512 ['xs','sm','md','lg'].map(function(size){
9513 if (settings[size]) {
9514 cfg.cls += ' col-' + size + '-' + settings[size];
9518 var inputblock = input;
9520 if(this.hasFeedback && !this.allowBlank){
9524 cls: 'glyphicon form-control-feedback'
9528 cls : 'has-feedback',
9537 if (this.before || this.after) {
9540 cls : 'input-group',
9544 inputblock.cn.push({
9546 cls : 'input-group-addon',
9551 inputblock.cn.push(input);
9553 if(this.hasFeedback && !this.allowBlank){
9554 inputblock.cls += ' has-feedback';
9555 inputblock.cn.push(feedback);
9559 inputblock.cn.push({
9561 cls : 'input-group-addon',
9568 if (align ==='left' && this.fieldLabel.length) {
9573 cls : 'control-label',
9574 html : this.fieldLabel
9585 if(this.labelWidth > 12){
9586 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9589 if(this.labelWidth < 13 && this.labelmd == 0){
9590 this.labelmd = this.labelWidth;
9593 if(this.labellg > 0){
9594 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9595 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9598 if(this.labelmd > 0){
9599 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9600 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9603 if(this.labelsm > 0){
9604 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9605 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9608 if(this.labelxs > 0){
9609 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9610 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9613 } else if ( this.fieldLabel.length) {
9618 //cls : 'input-group-addon',
9619 html : this.fieldLabel
9637 if (this.disabled) {
9638 input.disabled=true;
9645 * return the real textarea element.
9647 inputEl: function ()
9649 return this.el.select('textarea.form-control',true).first();
9653 * Clear any invalid styles/messages for this field
9655 clearInvalid : function()
9658 if(!this.el || this.preventMark){ // not rendered
9662 var label = this.el.select('label', true).first();
9663 var icon = this.el.select('i.fa-star', true).first();
9669 this.el.removeClass(this.invalidClass);
9671 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9673 var feedback = this.el.select('.form-control-feedback', true).first();
9676 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9681 this.fireEvent('valid', this);
9685 * Mark this field as valid
9687 markValid : function()
9689 if(!this.el || this.preventMark){ // not rendered
9693 this.el.removeClass([this.invalidClass, this.validClass]);
9695 var feedback = this.el.select('.form-control-feedback', true).first();
9698 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9701 if(this.disabled || this.allowBlank){
9705 var label = this.el.select('label', true).first();
9706 var icon = this.el.select('i.fa-star', true).first();
9712 this.el.addClass(this.validClass);
9714 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9716 var feedback = this.el.select('.form-control-feedback', true).first();
9719 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9720 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9725 this.fireEvent('valid', this);
9729 * Mark this field as invalid
9730 * @param {String} msg The validation message
9732 markInvalid : function(msg)
9734 if(!this.el || this.preventMark){ // not rendered
9738 this.el.removeClass([this.invalidClass, this.validClass]);
9740 var feedback = this.el.select('.form-control-feedback', true).first();
9743 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9746 if(this.disabled || this.allowBlank){
9750 var label = this.el.select('label', true).first();
9751 var icon = this.el.select('i.fa-star', true).first();
9753 if(!this.getValue().length && label && !icon){
9754 this.el.createChild({
9756 cls : 'text-danger fa fa-lg fa-star',
9757 tooltip : 'This field is required',
9758 style : 'margin-right:5px;'
9762 this.el.addClass(this.invalidClass);
9764 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9766 var feedback = this.el.select('.form-control-feedback', true).first();
9769 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9771 if(this.getValue().length || this.forceFeedback){
9772 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9779 this.fireEvent('invalid', this, msg);
9787 * trigger field - base class for combo..
9792 * @class Roo.bootstrap.TriggerField
9793 * @extends Roo.bootstrap.Input
9794 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9795 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9796 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9797 * for which you can provide a custom implementation. For example:
9799 var trigger = new Roo.bootstrap.TriggerField();
9800 trigger.onTriggerClick = myTriggerFn;
9801 trigger.applyTo('my-field');
9804 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9805 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9806 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
9807 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9808 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9811 * Create a new TriggerField.
9812 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9813 * to the base TextField)
9815 Roo.bootstrap.TriggerField = function(config){
9816 this.mimicing = false;
9817 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9820 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
9822 * @cfg {String} triggerClass A CSS class to apply to the trigger
9825 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9830 * @cfg {Boolean} removable (true|false) special filter default false
9834 /** @cfg {Boolean} grow @hide */
9835 /** @cfg {Number} growMin @hide */
9836 /** @cfg {Number} growMax @hide */
9842 autoSize: Roo.emptyFn,
9849 actionMode : 'wrap',
9854 getAutoCreate : function(){
9856 var align = this.labelAlign || this.parentLabelAlign();
9861 cls: 'form-group' //input-group
9868 type : this.inputType,
9869 cls : 'form-control',
9870 autocomplete: 'new-password',
9871 placeholder : this.placeholder || ''
9875 input.name = this.name;
9878 input.cls += ' input-' + this.size;
9881 if (this.disabled) {
9882 input.disabled=true;
9885 var inputblock = input;
9887 if(this.hasFeedback && !this.allowBlank){
9891 cls: 'glyphicon form-control-feedback'
9894 if(this.removable && !this.editable && !this.tickable){
9896 cls : 'has-feedback',
9902 cls : 'roo-combo-removable-btn close'
9909 cls : 'has-feedback',
9918 if(this.removable && !this.editable && !this.tickable){
9920 cls : 'roo-removable',
9926 cls : 'roo-combo-removable-btn close'
9933 if (this.before || this.after) {
9936 cls : 'input-group',
9940 inputblock.cn.push({
9942 cls : 'input-group-addon',
9947 inputblock.cn.push(input);
9949 if(this.hasFeedback && !this.allowBlank){
9950 inputblock.cls += ' has-feedback';
9951 inputblock.cn.push(feedback);
9955 inputblock.cn.push({
9957 cls : 'input-group-addon',
9970 cls: 'form-hidden-field'
9984 cls: 'form-hidden-field'
9988 cls: 'roo-select2-choices',
9992 cls: 'roo-select2-search-field',
10005 cls: 'roo-select2-container input-group',
10010 // cls: 'typeahead typeahead-long dropdown-menu',
10011 // style: 'display:none'
10016 if(!this.multiple && this.showToggleBtn){
10022 if (this.caret != false) {
10025 cls: 'fa fa-' + this.caret
10032 cls : 'input-group-addon btn dropdown-toggle',
10037 cls: 'combobox-clear',
10051 combobox.cls += ' roo-select2-container-multi';
10054 if (align ==='left' && this.fieldLabel.length) {
10056 cfg.cls += ' roo-form-group-label-left';
10061 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10062 tooltip : 'This field is required'
10067 cls : 'control-label',
10068 html : this.fieldLabel
10080 var labelCfg = cfg.cn[1];
10081 var contentCfg = cfg.cn[2];
10083 if(this.indicatorpos == 'right'){
10088 cls : 'control-label',
10092 html : this.fieldLabel
10096 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10097 tooltip : 'This field is required'
10110 labelCfg = cfg.cn[0];
10111 contentCfg = cfg.cn[1];
10114 if(this.labelWidth > 12){
10115 labelCfg.style = "width: " + this.labelWidth + 'px';
10118 if(this.labelWidth < 13 && this.labelmd == 0){
10119 this.labelmd = this.labelWidth;
10122 if(this.labellg > 0){
10123 labelCfg.cls += ' col-lg-' + this.labellg;
10124 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10127 if(this.labelmd > 0){
10128 labelCfg.cls += ' col-md-' + this.labelmd;
10129 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10132 if(this.labelsm > 0){
10133 labelCfg.cls += ' col-sm-' + this.labelsm;
10134 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10137 if(this.labelxs > 0){
10138 labelCfg.cls += ' col-xs-' + this.labelxs;
10139 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10142 } else if ( this.fieldLabel.length) {
10143 // Roo.log(" label");
10147 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10148 tooltip : 'This field is required'
10152 //cls : 'input-group-addon',
10153 html : this.fieldLabel
10161 if(this.indicatorpos == 'right'){
10169 html : this.fieldLabel
10173 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10174 tooltip : 'This field is required'
10187 // Roo.log(" no label && no align");
10194 ['xs','sm','md','lg'].map(function(size){
10195 if (settings[size]) {
10196 cfg.cls += ' col-' + size + '-' + settings[size];
10207 onResize : function(w, h){
10208 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10209 // if(typeof w == 'number'){
10210 // var x = w - this.trigger.getWidth();
10211 // this.inputEl().setWidth(this.adjustWidth('input', x));
10212 // this.trigger.setStyle('left', x+'px');
10217 adjustSize : Roo.BoxComponent.prototype.adjustSize,
10220 getResizeEl : function(){
10221 return this.inputEl();
10225 getPositionEl : function(){
10226 return this.inputEl();
10230 alignErrorIcon : function(){
10231 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10235 initEvents : function(){
10239 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10240 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10241 if(!this.multiple && this.showToggleBtn){
10242 this.trigger = this.el.select('span.dropdown-toggle',true).first();
10243 if(this.hideTrigger){
10244 this.trigger.setDisplayed(false);
10246 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10250 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10253 if(this.removable && !this.editable && !this.tickable){
10254 var close = this.closeTriggerEl();
10257 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10258 close.on('click', this.removeBtnClick, this, close);
10262 //this.trigger.addClassOnOver('x-form-trigger-over');
10263 //this.trigger.addClassOnClick('x-form-trigger-click');
10266 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10270 closeTriggerEl : function()
10272 var close = this.el.select('.roo-combo-removable-btn', true).first();
10273 return close ? close : false;
10276 removeBtnClick : function(e, h, el)
10278 e.preventDefault();
10280 if(this.fireEvent("remove", this) !== false){
10282 this.fireEvent("afterremove", this)
10286 createList : function()
10288 this.list = Roo.get(document.body).createChild({
10290 cls: 'typeahead typeahead-long dropdown-menu',
10291 style: 'display:none'
10294 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10299 initTrigger : function(){
10304 onDestroy : function(){
10306 this.trigger.removeAllListeners();
10307 // this.trigger.remove();
10310 // this.wrap.remove();
10312 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10316 onFocus : function(){
10317 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10319 if(!this.mimicing){
10320 this.wrap.addClass('x-trigger-wrap-focus');
10321 this.mimicing = true;
10322 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10323 if(this.monitorTab){
10324 this.el.on("keydown", this.checkTab, this);
10331 checkTab : function(e){
10332 if(e.getKey() == e.TAB){
10333 this.triggerBlur();
10338 onBlur : function(){
10343 mimicBlur : function(e, t){
10345 if(!this.wrap.contains(t) && this.validateBlur()){
10346 this.triggerBlur();
10352 triggerBlur : function(){
10353 this.mimicing = false;
10354 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10355 if(this.monitorTab){
10356 this.el.un("keydown", this.checkTab, this);
10358 //this.wrap.removeClass('x-trigger-wrap-focus');
10359 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10363 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10364 validateBlur : function(e, t){
10369 onDisable : function(){
10370 this.inputEl().dom.disabled = true;
10371 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10373 // this.wrap.addClass('x-item-disabled');
10378 onEnable : function(){
10379 this.inputEl().dom.disabled = false;
10380 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10382 // this.el.removeClass('x-item-disabled');
10387 onShow : function(){
10388 var ae = this.getActionEl();
10391 ae.dom.style.display = '';
10392 ae.dom.style.visibility = 'visible';
10398 onHide : function(){
10399 var ae = this.getActionEl();
10400 ae.dom.style.display = 'none';
10404 * The function that should handle the trigger's click event. This method does nothing by default until overridden
10405 * by an implementing function.
10407 * @param {EventObject} e
10409 onTriggerClick : Roo.emptyFn
10413 * Ext JS Library 1.1.1
10414 * Copyright(c) 2006-2007, Ext JS, LLC.
10416 * Originally Released Under LGPL - original licence link has changed is not relivant.
10419 * <script type="text/javascript">
10424 * @class Roo.data.SortTypes
10426 * Defines the default sorting (casting?) comparison functions used when sorting data.
10428 Roo.data.SortTypes = {
10430 * Default sort that does nothing
10431 * @param {Mixed} s The value being converted
10432 * @return {Mixed} The comparison value
10434 none : function(s){
10439 * The regular expression used to strip tags
10443 stripTagsRE : /<\/?[^>]+>/gi,
10446 * Strips all HTML tags to sort on text only
10447 * @param {Mixed} s The value being converted
10448 * @return {String} The comparison value
10450 asText : function(s){
10451 return String(s).replace(this.stripTagsRE, "");
10455 * Strips all HTML tags to sort on text only - Case insensitive
10456 * @param {Mixed} s The value being converted
10457 * @return {String} The comparison value
10459 asUCText : function(s){
10460 return String(s).toUpperCase().replace(this.stripTagsRE, "");
10464 * Case insensitive string
10465 * @param {Mixed} s The value being converted
10466 * @return {String} The comparison value
10468 asUCString : function(s) {
10469 return String(s).toUpperCase();
10474 * @param {Mixed} s The value being converted
10475 * @return {Number} The comparison value
10477 asDate : function(s) {
10481 if(s instanceof Date){
10482 return s.getTime();
10484 return Date.parse(String(s));
10489 * @param {Mixed} s The value being converted
10490 * @return {Float} The comparison value
10492 asFloat : function(s) {
10493 var val = parseFloat(String(s).replace(/,/g, ""));
10502 * @param {Mixed} s The value being converted
10503 * @return {Number} The comparison value
10505 asInt : function(s) {
10506 var val = parseInt(String(s).replace(/,/g, ""));
10514 * Ext JS Library 1.1.1
10515 * Copyright(c) 2006-2007, Ext JS, LLC.
10517 * Originally Released Under LGPL - original licence link has changed is not relivant.
10520 * <script type="text/javascript">
10524 * @class Roo.data.Record
10525 * Instances of this class encapsulate both record <em>definition</em> information, and record
10526 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10527 * to access Records cached in an {@link Roo.data.Store} object.<br>
10529 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10530 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10533 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10535 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10536 * {@link #create}. The parameters are the same.
10537 * @param {Array} data An associative Array of data values keyed by the field name.
10538 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10539 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10540 * not specified an integer id is generated.
10542 Roo.data.Record = function(data, id){
10543 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10548 * Generate a constructor for a specific record layout.
10549 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10550 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10551 * Each field definition object may contain the following properties: <ul>
10552 * <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,
10553 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10554 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10555 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10556 * is being used, then this is a string containing the javascript expression to reference the data relative to
10557 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10558 * to the data item relative to the record element. If the mapping expression is the same as the field name,
10559 * this may be omitted.</p></li>
10560 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10561 * <ul><li>auto (Default, implies no conversion)</li>
10566 * <li>date</li></ul></p></li>
10567 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10568 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10569 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10570 * by the Reader into an object that will be stored in the Record. It is passed the
10571 * following parameters:<ul>
10572 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10574 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10576 * <br>usage:<br><pre><code>
10577 var TopicRecord = Roo.data.Record.create(
10578 {name: 'title', mapping: 'topic_title'},
10579 {name: 'author', mapping: 'username'},
10580 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10581 {name: 'lastPost', mapping: 'post_time', type: 'date'},
10582 {name: 'lastPoster', mapping: 'user2'},
10583 {name: 'excerpt', mapping: 'post_text'}
10586 var myNewRecord = new TopicRecord({
10587 title: 'Do my job please',
10590 lastPost: new Date(),
10591 lastPoster: 'Animal',
10592 excerpt: 'No way dude!'
10594 myStore.add(myNewRecord);
10599 Roo.data.Record.create = function(o){
10600 var f = function(){
10601 f.superclass.constructor.apply(this, arguments);
10603 Roo.extend(f, Roo.data.Record);
10604 var p = f.prototype;
10605 p.fields = new Roo.util.MixedCollection(false, function(field){
10608 for(var i = 0, len = o.length; i < len; i++){
10609 p.fields.add(new Roo.data.Field(o[i]));
10611 f.getField = function(name){
10612 return p.fields.get(name);
10617 Roo.data.Record.AUTO_ID = 1000;
10618 Roo.data.Record.EDIT = 'edit';
10619 Roo.data.Record.REJECT = 'reject';
10620 Roo.data.Record.COMMIT = 'commit';
10622 Roo.data.Record.prototype = {
10624 * Readonly flag - true if this record has been modified.
10633 join : function(store){
10634 this.store = store;
10638 * Set the named field to the specified value.
10639 * @param {String} name The name of the field to set.
10640 * @param {Object} value The value to set the field to.
10642 set : function(name, value){
10643 if(this.data[name] == value){
10647 if(!this.modified){
10648 this.modified = {};
10650 if(typeof this.modified[name] == 'undefined'){
10651 this.modified[name] = this.data[name];
10653 this.data[name] = value;
10654 if(!this.editing && this.store){
10655 this.store.afterEdit(this);
10660 * Get the value of the named field.
10661 * @param {String} name The name of the field to get the value of.
10662 * @return {Object} The value of the field.
10664 get : function(name){
10665 return this.data[name];
10669 beginEdit : function(){
10670 this.editing = true;
10671 this.modified = {};
10675 cancelEdit : function(){
10676 this.editing = false;
10677 delete this.modified;
10681 endEdit : function(){
10682 this.editing = false;
10683 if(this.dirty && this.store){
10684 this.store.afterEdit(this);
10689 * Usually called by the {@link Roo.data.Store} which owns the Record.
10690 * Rejects all changes made to the Record since either creation, or the last commit operation.
10691 * Modified fields are reverted to their original values.
10693 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10694 * of reject operations.
10696 reject : function(){
10697 var m = this.modified;
10699 if(typeof m[n] != "function"){
10700 this.data[n] = m[n];
10703 this.dirty = false;
10704 delete this.modified;
10705 this.editing = false;
10707 this.store.afterReject(this);
10712 * Usually called by the {@link Roo.data.Store} which owns the Record.
10713 * Commits all changes made to the Record since either creation, or the last commit operation.
10715 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10716 * of commit operations.
10718 commit : function(){
10719 this.dirty = false;
10720 delete this.modified;
10721 this.editing = false;
10723 this.store.afterCommit(this);
10728 hasError : function(){
10729 return this.error != null;
10733 clearError : function(){
10738 * Creates a copy of this record.
10739 * @param {String} id (optional) A new record id if you don't want to use this record's id
10742 copy : function(newId) {
10743 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10747 * Ext JS Library 1.1.1
10748 * Copyright(c) 2006-2007, Ext JS, LLC.
10750 * Originally Released Under LGPL - original licence link has changed is not relivant.
10753 * <script type="text/javascript">
10759 * @class Roo.data.Store
10760 * @extends Roo.util.Observable
10761 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10762 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10764 * 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
10765 * has no knowledge of the format of the data returned by the Proxy.<br>
10767 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10768 * instances from the data object. These records are cached and made available through accessor functions.
10770 * Creates a new Store.
10771 * @param {Object} config A config object containing the objects needed for the Store to access data,
10772 * and read the data into Records.
10774 Roo.data.Store = function(config){
10775 this.data = new Roo.util.MixedCollection(false);
10776 this.data.getKey = function(o){
10779 this.baseParams = {};
10781 this.paramNames = {
10786 "multisort" : "_multisort"
10789 if(config && config.data){
10790 this.inlineData = config.data;
10791 delete config.data;
10794 Roo.apply(this, config);
10796 if(this.reader){ // reader passed
10797 this.reader = Roo.factory(this.reader, Roo.data);
10798 this.reader.xmodule = this.xmodule || false;
10799 if(!this.recordType){
10800 this.recordType = this.reader.recordType;
10802 if(this.reader.onMetaChange){
10803 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10807 if(this.recordType){
10808 this.fields = this.recordType.prototype.fields;
10810 this.modified = [];
10814 * @event datachanged
10815 * Fires when the data cache has changed, and a widget which is using this Store
10816 * as a Record cache should refresh its view.
10817 * @param {Store} this
10819 datachanged : true,
10821 * @event metachange
10822 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10823 * @param {Store} this
10824 * @param {Object} meta The JSON metadata
10829 * Fires when Records have been added to the Store
10830 * @param {Store} this
10831 * @param {Roo.data.Record[]} records The array of Records added
10832 * @param {Number} index The index at which the record(s) were added
10837 * Fires when a Record has been removed from the Store
10838 * @param {Store} this
10839 * @param {Roo.data.Record} record The Record that was removed
10840 * @param {Number} index The index at which the record was removed
10845 * Fires when a Record has been updated
10846 * @param {Store} this
10847 * @param {Roo.data.Record} record The Record that was updated
10848 * @param {String} operation The update operation being performed. Value may be one of:
10850 Roo.data.Record.EDIT
10851 Roo.data.Record.REJECT
10852 Roo.data.Record.COMMIT
10858 * Fires when the data cache has been cleared.
10859 * @param {Store} this
10863 * @event beforeload
10864 * Fires before a request is made for a new data object. If the beforeload handler returns false
10865 * the load action will be canceled.
10866 * @param {Store} this
10867 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10871 * @event beforeloadadd
10872 * Fires after a new set of Records has been loaded.
10873 * @param {Store} this
10874 * @param {Roo.data.Record[]} records The Records that were loaded
10875 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10877 beforeloadadd : true,
10880 * Fires after a new set of Records has been loaded, before they are added to the store.
10881 * @param {Store} this
10882 * @param {Roo.data.Record[]} records The Records that were loaded
10883 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10884 * @params {Object} return from reader
10888 * @event loadexception
10889 * Fires if an exception occurs in the Proxy during loading.
10890 * Called with the signature of the Proxy's "loadexception" event.
10891 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10894 * @param {Object} return from JsonData.reader() - success, totalRecords, records
10895 * @param {Object} load options
10896 * @param {Object} jsonData from your request (normally this contains the Exception)
10898 loadexception : true
10902 this.proxy = Roo.factory(this.proxy, Roo.data);
10903 this.proxy.xmodule = this.xmodule || false;
10904 this.relayEvents(this.proxy, ["loadexception"]);
10906 this.sortToggle = {};
10907 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10909 Roo.data.Store.superclass.constructor.call(this);
10911 if(this.inlineData){
10912 this.loadData(this.inlineData);
10913 delete this.inlineData;
10917 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10919 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
10920 * without a remote query - used by combo/forms at present.
10924 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10927 * @cfg {Array} data Inline data to be loaded when the store is initialized.
10930 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10931 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10934 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10935 * on any HTTP request
10938 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10941 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10945 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10946 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10948 remoteSort : false,
10951 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10952 * loaded or when a record is removed. (defaults to false).
10954 pruneModifiedRecords : false,
10957 lastOptions : null,
10960 * Add Records to the Store and fires the add event.
10961 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10963 add : function(records){
10964 records = [].concat(records);
10965 for(var i = 0, len = records.length; i < len; i++){
10966 records[i].join(this);
10968 var index = this.data.length;
10969 this.data.addAll(records);
10970 this.fireEvent("add", this, records, index);
10974 * Remove a Record from the Store and fires the remove event.
10975 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10977 remove : function(record){
10978 var index = this.data.indexOf(record);
10979 this.data.removeAt(index);
10980 if(this.pruneModifiedRecords){
10981 this.modified.remove(record);
10983 this.fireEvent("remove", this, record, index);
10987 * Remove all Records from the Store and fires the clear event.
10989 removeAll : function(){
10991 if(this.pruneModifiedRecords){
10992 this.modified = [];
10994 this.fireEvent("clear", this);
10998 * Inserts Records to the Store at the given index and fires the add event.
10999 * @param {Number} index The start index at which to insert the passed Records.
11000 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11002 insert : function(index, records){
11003 records = [].concat(records);
11004 for(var i = 0, len = records.length; i < len; i++){
11005 this.data.insert(index, records[i]);
11006 records[i].join(this);
11008 this.fireEvent("add", this, records, index);
11012 * Get the index within the cache of the passed Record.
11013 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11014 * @return {Number} The index of the passed Record. Returns -1 if not found.
11016 indexOf : function(record){
11017 return this.data.indexOf(record);
11021 * Get the index within the cache of the Record with the passed id.
11022 * @param {String} id The id of the Record to find.
11023 * @return {Number} The index of the Record. Returns -1 if not found.
11025 indexOfId : function(id){
11026 return this.data.indexOfKey(id);
11030 * Get the Record with the specified id.
11031 * @param {String} id The id of the Record to find.
11032 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11034 getById : function(id){
11035 return this.data.key(id);
11039 * Get the Record at the specified index.
11040 * @param {Number} index The index of the Record to find.
11041 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11043 getAt : function(index){
11044 return this.data.itemAt(index);
11048 * Returns a range of Records between specified indices.
11049 * @param {Number} startIndex (optional) The starting index (defaults to 0)
11050 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11051 * @return {Roo.data.Record[]} An array of Records
11053 getRange : function(start, end){
11054 return this.data.getRange(start, end);
11058 storeOptions : function(o){
11059 o = Roo.apply({}, o);
11062 this.lastOptions = o;
11066 * Loads the Record cache from the configured Proxy using the configured Reader.
11068 * If using remote paging, then the first load call must specify the <em>start</em>
11069 * and <em>limit</em> properties in the options.params property to establish the initial
11070 * position within the dataset, and the number of Records to cache on each read from the Proxy.
11072 * <strong>It is important to note that for remote data sources, loading is asynchronous,
11073 * and this call will return before the new data has been loaded. Perform any post-processing
11074 * in a callback function, or in a "load" event handler.</strong>
11076 * @param {Object} options An object containing properties which control loading options:<ul>
11077 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11078 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11079 * passed the following arguments:<ul>
11080 * <li>r : Roo.data.Record[]</li>
11081 * <li>options: Options object from the load call</li>
11082 * <li>success: Boolean success indicator</li></ul></li>
11083 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11084 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11087 load : function(options){
11088 options = options || {};
11089 if(this.fireEvent("beforeload", this, options) !== false){
11090 this.storeOptions(options);
11091 var p = Roo.apply(options.params || {}, this.baseParams);
11092 // if meta was not loaded from remote source.. try requesting it.
11093 if (!this.reader.metaFromRemote) {
11094 p._requestMeta = 1;
11096 if(this.sortInfo && this.remoteSort){
11097 var pn = this.paramNames;
11098 p[pn["sort"]] = this.sortInfo.field;
11099 p[pn["dir"]] = this.sortInfo.direction;
11101 if (this.multiSort) {
11102 var pn = this.paramNames;
11103 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11106 this.proxy.load(p, this.reader, this.loadRecords, this, options);
11111 * Reloads the Record cache from the configured Proxy using the configured Reader and
11112 * the options from the last load operation performed.
11113 * @param {Object} options (optional) An object containing properties which may override the options
11114 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11115 * the most recently used options are reused).
11117 reload : function(options){
11118 this.load(Roo.applyIf(options||{}, this.lastOptions));
11122 // Called as a callback by the Reader during a load operation.
11123 loadRecords : function(o, options, success){
11124 if(!o || success === false){
11125 if(success !== false){
11126 this.fireEvent("load", this, [], options, o);
11128 if(options.callback){
11129 options.callback.call(options.scope || this, [], options, false);
11133 // if data returned failure - throw an exception.
11134 if (o.success === false) {
11135 // show a message if no listener is registered.
11136 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11137 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11139 // loadmask wil be hooked into this..
11140 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11143 var r = o.records, t = o.totalRecords || r.length;
11145 this.fireEvent("beforeloadadd", this, r, options, o);
11147 if(!options || options.add !== true){
11148 if(this.pruneModifiedRecords){
11149 this.modified = [];
11151 for(var i = 0, len = r.length; i < len; i++){
11155 this.data = this.snapshot;
11156 delete this.snapshot;
11159 this.data.addAll(r);
11160 this.totalLength = t;
11162 this.fireEvent("datachanged", this);
11164 this.totalLength = Math.max(t, this.data.length+r.length);
11168 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11170 var e = new Roo.data.Record({});
11172 e.set(this.parent.displayField, this.parent.emptyTitle);
11173 e.set(this.parent.valueField, '');
11178 this.fireEvent("load", this, r, options, o);
11179 if(options.callback){
11180 options.callback.call(options.scope || this, r, options, true);
11186 * Loads data from a passed data block. A Reader which understands the format of the data
11187 * must have been configured in the constructor.
11188 * @param {Object} data The data block from which to read the Records. The format of the data expected
11189 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11190 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11192 loadData : function(o, append){
11193 var r = this.reader.readRecords(o);
11194 this.loadRecords(r, {add: append}, true);
11198 * Gets the number of cached records.
11200 * <em>If using paging, this may not be the total size of the dataset. If the data object
11201 * used by the Reader contains the dataset size, then the getTotalCount() function returns
11202 * the data set size</em>
11204 getCount : function(){
11205 return this.data.length || 0;
11209 * Gets the total number of records in the dataset as returned by the server.
11211 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11212 * the dataset size</em>
11214 getTotalCount : function(){
11215 return this.totalLength || 0;
11219 * Returns the sort state of the Store as an object with two properties:
11221 field {String} The name of the field by which the Records are sorted
11222 direction {String} The sort order, "ASC" or "DESC"
11225 getSortState : function(){
11226 return this.sortInfo;
11230 applySort : function(){
11231 if(this.sortInfo && !this.remoteSort){
11232 var s = this.sortInfo, f = s.field;
11233 var st = this.fields.get(f).sortType;
11234 var fn = function(r1, r2){
11235 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11236 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11238 this.data.sort(s.direction, fn);
11239 if(this.snapshot && this.snapshot != this.data){
11240 this.snapshot.sort(s.direction, fn);
11246 * Sets the default sort column and order to be used by the next load operation.
11247 * @param {String} fieldName The name of the field to sort by.
11248 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11250 setDefaultSort : function(field, dir){
11251 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11255 * Sort the Records.
11256 * If remote sorting is used, the sort is performed on the server, and the cache is
11257 * reloaded. If local sorting is used, the cache is sorted internally.
11258 * @param {String} fieldName The name of the field to sort by.
11259 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11261 sort : function(fieldName, dir){
11262 var f = this.fields.get(fieldName);
11264 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11266 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11267 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11272 this.sortToggle[f.name] = dir;
11273 this.sortInfo = {field: f.name, direction: dir};
11274 if(!this.remoteSort){
11276 this.fireEvent("datachanged", this);
11278 this.load(this.lastOptions);
11283 * Calls the specified function for each of the Records in the cache.
11284 * @param {Function} fn The function to call. The Record is passed as the first parameter.
11285 * Returning <em>false</em> aborts and exits the iteration.
11286 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11288 each : function(fn, scope){
11289 this.data.each(fn, scope);
11293 * Gets all records modified since the last commit. Modified records are persisted across load operations
11294 * (e.g., during paging).
11295 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11297 getModifiedRecords : function(){
11298 return this.modified;
11302 createFilterFn : function(property, value, anyMatch){
11303 if(!value.exec){ // not a regex
11304 value = String(value);
11305 if(value.length == 0){
11308 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11310 return function(r){
11311 return value.test(r.data[property]);
11316 * Sums the value of <i>property</i> for each record between start and end and returns the result.
11317 * @param {String} property A field on your records
11318 * @param {Number} start The record index to start at (defaults to 0)
11319 * @param {Number} end The last record index to include (defaults to length - 1)
11320 * @return {Number} The sum
11322 sum : function(property, start, end){
11323 var rs = this.data.items, v = 0;
11324 start = start || 0;
11325 end = (end || end === 0) ? end : rs.length-1;
11327 for(var i = start; i <= end; i++){
11328 v += (rs[i].data[property] || 0);
11334 * Filter the records by a specified property.
11335 * @param {String} field A field on your records
11336 * @param {String/RegExp} value Either a string that the field
11337 * should start with or a RegExp to test against the field
11338 * @param {Boolean} anyMatch True to match any part not just the beginning
11340 filter : function(property, value, anyMatch){
11341 var fn = this.createFilterFn(property, value, anyMatch);
11342 return fn ? this.filterBy(fn) : this.clearFilter();
11346 * Filter by a function. The specified function will be called with each
11347 * record in this data source. If the function returns true the record is included,
11348 * otherwise it is filtered.
11349 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11350 * @param {Object} scope (optional) The scope of the function (defaults to this)
11352 filterBy : function(fn, scope){
11353 this.snapshot = this.snapshot || this.data;
11354 this.data = this.queryBy(fn, scope||this);
11355 this.fireEvent("datachanged", this);
11359 * Query the records by a specified property.
11360 * @param {String} field A field on your records
11361 * @param {String/RegExp} value Either a string that the field
11362 * should start with or a RegExp to test against the field
11363 * @param {Boolean} anyMatch True to match any part not just the beginning
11364 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11366 query : function(property, value, anyMatch){
11367 var fn = this.createFilterFn(property, value, anyMatch);
11368 return fn ? this.queryBy(fn) : this.data.clone();
11372 * Query by a function. The specified function will be called with each
11373 * record in this data source. If the function returns true the record is included
11375 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11376 * @param {Object} scope (optional) The scope of the function (defaults to this)
11377 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11379 queryBy : function(fn, scope){
11380 var data = this.snapshot || this.data;
11381 return data.filterBy(fn, scope||this);
11385 * Collects unique values for a particular dataIndex from this store.
11386 * @param {String} dataIndex The property to collect
11387 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11388 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11389 * @return {Array} An array of the unique values
11391 collect : function(dataIndex, allowNull, bypassFilter){
11392 var d = (bypassFilter === true && this.snapshot) ?
11393 this.snapshot.items : this.data.items;
11394 var v, sv, r = [], l = {};
11395 for(var i = 0, len = d.length; i < len; i++){
11396 v = d[i].data[dataIndex];
11398 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11407 * Revert to a view of the Record cache with no filtering applied.
11408 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11410 clearFilter : function(suppressEvent){
11411 if(this.snapshot && this.snapshot != this.data){
11412 this.data = this.snapshot;
11413 delete this.snapshot;
11414 if(suppressEvent !== true){
11415 this.fireEvent("datachanged", this);
11421 afterEdit : function(record){
11422 if(this.modified.indexOf(record) == -1){
11423 this.modified.push(record);
11425 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11429 afterReject : function(record){
11430 this.modified.remove(record);
11431 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11435 afterCommit : function(record){
11436 this.modified.remove(record);
11437 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11441 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11442 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11444 commitChanges : function(){
11445 var m = this.modified.slice(0);
11446 this.modified = [];
11447 for(var i = 0, len = m.length; i < len; i++){
11453 * Cancel outstanding changes on all changed records.
11455 rejectChanges : function(){
11456 var m = this.modified.slice(0);
11457 this.modified = [];
11458 for(var i = 0, len = m.length; i < len; i++){
11463 onMetaChange : function(meta, rtype, o){
11464 this.recordType = rtype;
11465 this.fields = rtype.prototype.fields;
11466 delete this.snapshot;
11467 this.sortInfo = meta.sortInfo || this.sortInfo;
11468 this.modified = [];
11469 this.fireEvent('metachange', this, this.reader.meta);
11472 moveIndex : function(data, type)
11474 var index = this.indexOf(data);
11476 var newIndex = index + type;
11480 this.insert(newIndex, data);
11485 * Ext JS Library 1.1.1
11486 * Copyright(c) 2006-2007, Ext JS, LLC.
11488 * Originally Released Under LGPL - original licence link has changed is not relivant.
11491 * <script type="text/javascript">
11495 * @class Roo.data.SimpleStore
11496 * @extends Roo.data.Store
11497 * Small helper class to make creating Stores from Array data easier.
11498 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11499 * @cfg {Array} fields An array of field definition objects, or field name strings.
11500 * @cfg {Array} data The multi-dimensional array of data
11502 * @param {Object} config
11504 Roo.data.SimpleStore = function(config){
11505 Roo.data.SimpleStore.superclass.constructor.call(this, {
11507 reader: new Roo.data.ArrayReader({
11510 Roo.data.Record.create(config.fields)
11512 proxy : new Roo.data.MemoryProxy(config.data)
11516 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11518 * Ext JS Library 1.1.1
11519 * Copyright(c) 2006-2007, Ext JS, LLC.
11521 * Originally Released Under LGPL - original licence link has changed is not relivant.
11524 * <script type="text/javascript">
11529 * @extends Roo.data.Store
11530 * @class Roo.data.JsonStore
11531 * Small helper class to make creating Stores for JSON data easier. <br/>
11533 var store = new Roo.data.JsonStore({
11534 url: 'get-images.php',
11536 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11539 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11540 * JsonReader and HttpProxy (unless inline data is provided).</b>
11541 * @cfg {Array} fields An array of field definition objects, or field name strings.
11543 * @param {Object} config
11545 Roo.data.JsonStore = function(c){
11546 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11547 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11548 reader: new Roo.data.JsonReader(c, c.fields)
11551 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11553 * Ext JS Library 1.1.1
11554 * Copyright(c) 2006-2007, Ext JS, LLC.
11556 * Originally Released Under LGPL - original licence link has changed is not relivant.
11559 * <script type="text/javascript">
11563 Roo.data.Field = function(config){
11564 if(typeof config == "string"){
11565 config = {name: config};
11567 Roo.apply(this, config);
11570 this.type = "auto";
11573 var st = Roo.data.SortTypes;
11574 // named sortTypes are supported, here we look them up
11575 if(typeof this.sortType == "string"){
11576 this.sortType = st[this.sortType];
11579 // set default sortType for strings and dates
11580 if(!this.sortType){
11583 this.sortType = st.asUCString;
11586 this.sortType = st.asDate;
11589 this.sortType = st.none;
11594 var stripRe = /[\$,%]/g;
11596 // prebuilt conversion function for this field, instead of
11597 // switching every time we're reading a value
11599 var cv, dateFormat = this.dateFormat;
11604 cv = function(v){ return v; };
11607 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11611 return v !== undefined && v !== null && v !== '' ?
11612 parseInt(String(v).replace(stripRe, ""), 10) : '';
11617 return v !== undefined && v !== null && v !== '' ?
11618 parseFloat(String(v).replace(stripRe, ""), 10) : '';
11623 cv = function(v){ return v === true || v === "true" || v == 1; };
11630 if(v instanceof Date){
11634 if(dateFormat == "timestamp"){
11635 return new Date(v*1000);
11637 return Date.parseDate(v, dateFormat);
11639 var parsed = Date.parse(v);
11640 return parsed ? new Date(parsed) : null;
11649 Roo.data.Field.prototype = {
11657 * Ext JS Library 1.1.1
11658 * Copyright(c) 2006-2007, Ext JS, LLC.
11660 * Originally Released Under LGPL - original licence link has changed is not relivant.
11663 * <script type="text/javascript">
11666 // Base class for reading structured data from a data source. This class is intended to be
11667 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11670 * @class Roo.data.DataReader
11671 * Base class for reading structured data from a data source. This class is intended to be
11672 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11675 Roo.data.DataReader = function(meta, recordType){
11679 this.recordType = recordType instanceof Array ?
11680 Roo.data.Record.create(recordType) : recordType;
11683 Roo.data.DataReader.prototype = {
11685 * Create an empty record
11686 * @param {Object} data (optional) - overlay some values
11687 * @return {Roo.data.Record} record created.
11689 newRow : function(d) {
11691 this.recordType.prototype.fields.each(function(c) {
11693 case 'int' : da[c.name] = 0; break;
11694 case 'date' : da[c.name] = new Date(); break;
11695 case 'float' : da[c.name] = 0.0; break;
11696 case 'boolean' : da[c.name] = false; break;
11697 default : da[c.name] = ""; break;
11701 return new this.recordType(Roo.apply(da, d));
11706 * Ext JS Library 1.1.1
11707 * Copyright(c) 2006-2007, Ext JS, LLC.
11709 * Originally Released Under LGPL - original licence link has changed is not relivant.
11712 * <script type="text/javascript">
11716 * @class Roo.data.DataProxy
11717 * @extends Roo.data.Observable
11718 * This class is an abstract base class for implementations which provide retrieval of
11719 * unformatted data objects.<br>
11721 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11722 * (of the appropriate type which knows how to parse the data object) to provide a block of
11723 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11725 * Custom implementations must implement the load method as described in
11726 * {@link Roo.data.HttpProxy#load}.
11728 Roo.data.DataProxy = function(){
11731 * @event beforeload
11732 * Fires before a network request is made to retrieve a data object.
11733 * @param {Object} This DataProxy object.
11734 * @param {Object} params The params parameter to the load function.
11739 * Fires before the load method's callback is called.
11740 * @param {Object} This DataProxy object.
11741 * @param {Object} o The data object.
11742 * @param {Object} arg The callback argument object passed to the load function.
11746 * @event loadexception
11747 * Fires if an Exception occurs during data retrieval.
11748 * @param {Object} This DataProxy object.
11749 * @param {Object} o The data object.
11750 * @param {Object} arg The callback argument object passed to the load function.
11751 * @param {Object} e The Exception.
11753 loadexception : true
11755 Roo.data.DataProxy.superclass.constructor.call(this);
11758 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11761 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11765 * Ext JS Library 1.1.1
11766 * Copyright(c) 2006-2007, Ext JS, LLC.
11768 * Originally Released Under LGPL - original licence link has changed is not relivant.
11771 * <script type="text/javascript">
11774 * @class Roo.data.MemoryProxy
11775 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11776 * to the Reader when its load method is called.
11778 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11780 Roo.data.MemoryProxy = function(data){
11784 Roo.data.MemoryProxy.superclass.constructor.call(this);
11788 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11791 * Load data from the requested source (in this case an in-memory
11792 * data object passed to the constructor), read the data object into
11793 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11794 * process that block using the passed callback.
11795 * @param {Object} params This parameter is not used by the MemoryProxy class.
11796 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11797 * object into a block of Roo.data.Records.
11798 * @param {Function} callback The function into which to pass the block of Roo.data.records.
11799 * The function must be passed <ul>
11800 * <li>The Record block object</li>
11801 * <li>The "arg" argument from the load function</li>
11802 * <li>A boolean success indicator</li>
11804 * @param {Object} scope The scope in which to call the callback
11805 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11807 load : function(params, reader, callback, scope, arg){
11808 params = params || {};
11811 result = reader.readRecords(this.data);
11813 this.fireEvent("loadexception", this, arg, null, e);
11814 callback.call(scope, null, arg, false);
11817 callback.call(scope, result, arg, true);
11821 update : function(params, records){
11826 * Ext JS Library 1.1.1
11827 * Copyright(c) 2006-2007, Ext JS, LLC.
11829 * Originally Released Under LGPL - original licence link has changed is not relivant.
11832 * <script type="text/javascript">
11835 * @class Roo.data.HttpProxy
11836 * @extends Roo.data.DataProxy
11837 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11838 * configured to reference a certain URL.<br><br>
11840 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11841 * from which the running page was served.<br><br>
11843 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11845 * Be aware that to enable the browser to parse an XML document, the server must set
11846 * the Content-Type header in the HTTP response to "text/xml".
11848 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11849 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
11850 * will be used to make the request.
11852 Roo.data.HttpProxy = function(conn){
11853 Roo.data.HttpProxy.superclass.constructor.call(this);
11854 // is conn a conn config or a real conn?
11856 this.useAjax = !conn || !conn.events;
11860 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11861 // thse are take from connection...
11864 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11867 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11868 * extra parameters to each request made by this object. (defaults to undefined)
11871 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11872 * to each request made by this object. (defaults to undefined)
11875 * @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)
11878 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11881 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11887 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11891 * Return the {@link Roo.data.Connection} object being used by this Proxy.
11892 * @return {Connection} The Connection object. This object may be used to subscribe to events on
11893 * a finer-grained basis than the DataProxy events.
11895 getConnection : function(){
11896 return this.useAjax ? Roo.Ajax : this.conn;
11900 * Load data from the configured {@link Roo.data.Connection}, read the data object into
11901 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11902 * process that block using the passed callback.
11903 * @param {Object} params An object containing properties which are to be used as HTTP parameters
11904 * for the request to the remote server.
11905 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11906 * object into a block of Roo.data.Records.
11907 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11908 * The function must be passed <ul>
11909 * <li>The Record block object</li>
11910 * <li>The "arg" argument from the load function</li>
11911 * <li>A boolean success indicator</li>
11913 * @param {Object} scope The scope in which to call the callback
11914 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11916 load : function(params, reader, callback, scope, arg){
11917 if(this.fireEvent("beforeload", this, params) !== false){
11919 params : params || {},
11921 callback : callback,
11926 callback : this.loadResponse,
11930 Roo.applyIf(o, this.conn);
11931 if(this.activeRequest){
11932 Roo.Ajax.abort(this.activeRequest);
11934 this.activeRequest = Roo.Ajax.request(o);
11936 this.conn.request(o);
11939 callback.call(scope||this, null, arg, false);
11944 loadResponse : function(o, success, response){
11945 delete this.activeRequest;
11947 this.fireEvent("loadexception", this, o, response);
11948 o.request.callback.call(o.request.scope, null, o.request.arg, false);
11953 result = o.reader.read(response);
11955 this.fireEvent("loadexception", this, o, response, e);
11956 o.request.callback.call(o.request.scope, null, o.request.arg, false);
11960 this.fireEvent("load", this, o, o.request.arg);
11961 o.request.callback.call(o.request.scope, result, o.request.arg, true);
11965 update : function(dataSet){
11970 updateResponse : function(dataSet){
11975 * Ext JS Library 1.1.1
11976 * Copyright(c) 2006-2007, Ext JS, LLC.
11978 * Originally Released Under LGPL - original licence link has changed is not relivant.
11981 * <script type="text/javascript">
11985 * @class Roo.data.ScriptTagProxy
11986 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11987 * other than the originating domain of the running page.<br><br>
11989 * <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
11990 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11992 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11993 * source code that is used as the source inside a <script> tag.<br><br>
11995 * In order for the browser to process the returned data, the server must wrap the data object
11996 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11997 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11998 * depending on whether the callback name was passed:
12001 boolean scriptTag = false;
12002 String cb = request.getParameter("callback");
12005 response.setContentType("text/javascript");
12007 response.setContentType("application/x-json");
12009 Writer out = response.getWriter();
12011 out.write(cb + "(");
12013 out.print(dataBlock.toJsonString());
12020 * @param {Object} config A configuration object.
12022 Roo.data.ScriptTagProxy = function(config){
12023 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12024 Roo.apply(this, config);
12025 this.head = document.getElementsByTagName("head")[0];
12028 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12030 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12032 * @cfg {String} url The URL from which to request the data object.
12035 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12039 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12040 * the server the name of the callback function set up by the load call to process the returned data object.
12041 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12042 * javascript output which calls this named function passing the data object as its only parameter.
12044 callbackParam : "callback",
12046 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12047 * name to the request.
12052 * Load data from the configured URL, read the data object into
12053 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12054 * process that block using the passed callback.
12055 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12056 * for the request to the remote server.
12057 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12058 * object into a block of Roo.data.Records.
12059 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12060 * The function must be passed <ul>
12061 * <li>The Record block object</li>
12062 * <li>The "arg" argument from the load function</li>
12063 * <li>A boolean success indicator</li>
12065 * @param {Object} scope The scope in which to call the callback
12066 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12068 load : function(params, reader, callback, scope, arg){
12069 if(this.fireEvent("beforeload", this, params) !== false){
12071 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12073 var url = this.url;
12074 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12076 url += "&_dc=" + (new Date().getTime());
12078 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12081 cb : "stcCallback"+transId,
12082 scriptId : "stcScript"+transId,
12086 callback : callback,
12092 window[trans.cb] = function(o){
12093 conn.handleResponse(o, trans);
12096 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12098 if(this.autoAbort !== false){
12102 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12104 var script = document.createElement("script");
12105 script.setAttribute("src", url);
12106 script.setAttribute("type", "text/javascript");
12107 script.setAttribute("id", trans.scriptId);
12108 this.head.appendChild(script);
12110 this.trans = trans;
12112 callback.call(scope||this, null, arg, false);
12117 isLoading : function(){
12118 return this.trans ? true : false;
12122 * Abort the current server request.
12124 abort : function(){
12125 if(this.isLoading()){
12126 this.destroyTrans(this.trans);
12131 destroyTrans : function(trans, isLoaded){
12132 this.head.removeChild(document.getElementById(trans.scriptId));
12133 clearTimeout(trans.timeoutId);
12135 window[trans.cb] = undefined;
12137 delete window[trans.cb];
12140 // if hasn't been loaded, wait for load to remove it to prevent script error
12141 window[trans.cb] = function(){
12142 window[trans.cb] = undefined;
12144 delete window[trans.cb];
12151 handleResponse : function(o, trans){
12152 this.trans = false;
12153 this.destroyTrans(trans, true);
12156 result = trans.reader.readRecords(o);
12158 this.fireEvent("loadexception", this, o, trans.arg, e);
12159 trans.callback.call(trans.scope||window, null, trans.arg, false);
12162 this.fireEvent("load", this, o, trans.arg);
12163 trans.callback.call(trans.scope||window, result, trans.arg, true);
12167 handleFailure : function(trans){
12168 this.trans = false;
12169 this.destroyTrans(trans, false);
12170 this.fireEvent("loadexception", this, null, trans.arg);
12171 trans.callback.call(trans.scope||window, null, trans.arg, false);
12175 * Ext JS Library 1.1.1
12176 * Copyright(c) 2006-2007, Ext JS, LLC.
12178 * Originally Released Under LGPL - original licence link has changed is not relivant.
12181 * <script type="text/javascript">
12185 * @class Roo.data.JsonReader
12186 * @extends Roo.data.DataReader
12187 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12188 * based on mappings in a provided Roo.data.Record constructor.
12190 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12191 * in the reply previously.
12196 var RecordDef = Roo.data.Record.create([
12197 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
12198 {name: 'occupation'} // This field will use "occupation" as the mapping.
12200 var myReader = new Roo.data.JsonReader({
12201 totalProperty: "results", // The property which contains the total dataset size (optional)
12202 root: "rows", // The property which contains an Array of row objects
12203 id: "id" // The property within each row object that provides an ID for the record (optional)
12207 * This would consume a JSON file like this:
12209 { 'results': 2, 'rows': [
12210 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12211 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12214 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12215 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12216 * paged from the remote server.
12217 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12218 * @cfg {String} root name of the property which contains the Array of row objects.
12219 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12220 * @cfg {Array} fields Array of field definition objects
12222 * Create a new JsonReader
12223 * @param {Object} meta Metadata configuration options
12224 * @param {Object} recordType Either an Array of field definition objects,
12225 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12227 Roo.data.JsonReader = function(meta, recordType){
12230 // set some defaults:
12231 Roo.applyIf(meta, {
12232 totalProperty: 'total',
12233 successProperty : 'success',
12238 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12240 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12243 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
12244 * Used by Store query builder to append _requestMeta to params.
12247 metaFromRemote : false,
12249 * This method is only used by a DataProxy which has retrieved data from a remote server.
12250 * @param {Object} response The XHR object which contains the JSON data in its responseText.
12251 * @return {Object} data A data block which is used by an Roo.data.Store object as
12252 * a cache of Roo.data.Records.
12254 read : function(response){
12255 var json = response.responseText;
12257 var o = /* eval:var:o */ eval("("+json+")");
12259 throw {message: "JsonReader.read: Json object not found"};
12265 this.metaFromRemote = true;
12266 this.meta = o.metaData;
12267 this.recordType = Roo.data.Record.create(o.metaData.fields);
12268 this.onMetaChange(this.meta, this.recordType, o);
12270 return this.readRecords(o);
12273 // private function a store will implement
12274 onMetaChange : function(meta, recordType, o){
12281 simpleAccess: function(obj, subsc) {
12288 getJsonAccessor: function(){
12290 return function(expr) {
12292 return(re.test(expr))
12293 ? new Function("obj", "return obj." + expr)
12298 return Roo.emptyFn;
12303 * Create a data block containing Roo.data.Records from an XML document.
12304 * @param {Object} o An object which contains an Array of row objects in the property specified
12305 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12306 * which contains the total size of the dataset.
12307 * @return {Object} data A data block which is used by an Roo.data.Store object as
12308 * a cache of Roo.data.Records.
12310 readRecords : function(o){
12312 * After any data loads, the raw JSON data is available for further custom processing.
12316 var s = this.meta, Record = this.recordType,
12317 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12319 // Generate extraction functions for the totalProperty, the root, the id, and for each field
12321 if(s.totalProperty) {
12322 this.getTotal = this.getJsonAccessor(s.totalProperty);
12324 if(s.successProperty) {
12325 this.getSuccess = this.getJsonAccessor(s.successProperty);
12327 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12329 var g = this.getJsonAccessor(s.id);
12330 this.getId = function(rec) {
12332 return (r === undefined || r === "") ? null : r;
12335 this.getId = function(){return null;};
12338 for(var jj = 0; jj < fl; jj++){
12340 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12341 this.ef[jj] = this.getJsonAccessor(map);
12345 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12346 if(s.totalProperty){
12347 var vt = parseInt(this.getTotal(o), 10);
12352 if(s.successProperty){
12353 var vs = this.getSuccess(o);
12354 if(vs === false || vs === 'false'){
12359 for(var i = 0; i < c; i++){
12362 var id = this.getId(n);
12363 for(var j = 0; j < fl; j++){
12365 var v = this.ef[j](n);
12367 Roo.log('missing convert for ' + f.name);
12371 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12373 var record = new Record(values, id);
12375 records[i] = record;
12381 totalRecords : totalRecords
12386 * Ext JS Library 1.1.1
12387 * Copyright(c) 2006-2007, Ext JS, LLC.
12389 * Originally Released Under LGPL - original licence link has changed is not relivant.
12392 * <script type="text/javascript">
12396 * @class Roo.data.ArrayReader
12397 * @extends Roo.data.DataReader
12398 * Data reader class to create an Array of Roo.data.Record objects from an Array.
12399 * Each element of that Array represents a row of data fields. The
12400 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12401 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12405 var RecordDef = Roo.data.Record.create([
12406 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
12407 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
12409 var myReader = new Roo.data.ArrayReader({
12410 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
12414 * This would consume an Array like this:
12416 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12418 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12420 * Create a new JsonReader
12421 * @param {Object} meta Metadata configuration options.
12422 * @param {Object} recordType Either an Array of field definition objects
12423 * as specified to {@link Roo.data.Record#create},
12424 * or an {@link Roo.data.Record} object
12425 * created using {@link Roo.data.Record#create}.
12427 Roo.data.ArrayReader = function(meta, recordType){
12428 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12431 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12433 * Create a data block containing Roo.data.Records from an XML document.
12434 * @param {Object} o An Array of row objects which represents the dataset.
12435 * @return {Object} data A data block which is used by an Roo.data.Store object as
12436 * a cache of Roo.data.Records.
12438 readRecords : function(o){
12439 var sid = this.meta ? this.meta.id : null;
12440 var recordType = this.recordType, fields = recordType.prototype.fields;
12443 for(var i = 0; i < root.length; i++){
12446 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12447 for(var j = 0, jlen = fields.length; j < jlen; j++){
12448 var f = fields.items[j];
12449 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12450 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12452 values[f.name] = v;
12454 var record = new recordType(values, id);
12456 records[records.length] = record;
12460 totalRecords : records.length
12469 * @class Roo.bootstrap.ComboBox
12470 * @extends Roo.bootstrap.TriggerField
12471 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12472 * @cfg {Boolean} append (true|false) default false
12473 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12474 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12475 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12476 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12477 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12478 * @cfg {Boolean} animate default true
12479 * @cfg {Boolean} emptyResultText only for touch device
12480 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12481 * @cfg {String} emptyTitle default ''
12483 * Create a new ComboBox.
12484 * @param {Object} config Configuration options
12486 Roo.bootstrap.ComboBox = function(config){
12487 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12491 * Fires when the dropdown list is expanded
12492 * @param {Roo.bootstrap.ComboBox} combo This combo box
12497 * Fires when the dropdown list is collapsed
12498 * @param {Roo.bootstrap.ComboBox} combo This combo box
12502 * @event beforeselect
12503 * Fires before a list item is selected. Return false to cancel the selection.
12504 * @param {Roo.bootstrap.ComboBox} combo This combo box
12505 * @param {Roo.data.Record} record The data record returned from the underlying store
12506 * @param {Number} index The index of the selected item in the dropdown list
12508 'beforeselect' : true,
12511 * Fires when a list item is selected
12512 * @param {Roo.bootstrap.ComboBox} combo This combo box
12513 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12514 * @param {Number} index The index of the selected item in the dropdown list
12518 * @event beforequery
12519 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12520 * The event object passed has these properties:
12521 * @param {Roo.bootstrap.ComboBox} combo This combo box
12522 * @param {String} query The query
12523 * @param {Boolean} forceAll true to force "all" query
12524 * @param {Boolean} cancel true to cancel the query
12525 * @param {Object} e The query event object
12527 'beforequery': true,
12530 * Fires when the 'add' icon is pressed (add a listener to enable add button)
12531 * @param {Roo.bootstrap.ComboBox} combo This combo box
12536 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12537 * @param {Roo.bootstrap.ComboBox} combo This combo box
12538 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12543 * Fires when the remove value from the combobox array
12544 * @param {Roo.bootstrap.ComboBox} combo This combo box
12548 * @event afterremove
12549 * Fires when the remove value from the combobox array
12550 * @param {Roo.bootstrap.ComboBox} combo This combo box
12552 'afterremove' : true,
12554 * @event specialfilter
12555 * Fires when specialfilter
12556 * @param {Roo.bootstrap.ComboBox} combo This combo box
12558 'specialfilter' : true,
12561 * Fires when tick the element
12562 * @param {Roo.bootstrap.ComboBox} combo This combo box
12566 * @event touchviewdisplay
12567 * Fires when touch view require special display (default is using displayField)
12568 * @param {Roo.bootstrap.ComboBox} combo This combo box
12569 * @param {Object} cfg set html .
12571 'touchviewdisplay' : true
12576 this.tickItems = [];
12578 this.selectedIndex = -1;
12579 if(this.mode == 'local'){
12580 if(config.queryDelay === undefined){
12581 this.queryDelay = 10;
12583 if(config.minChars === undefined){
12589 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12592 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12593 * rendering into an Roo.Editor, defaults to false)
12596 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12597 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12600 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12603 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12604 * the dropdown list (defaults to undefined, with no header element)
12608 * @cfg {String/Roo.Template} tpl The template to use to render the output
12612 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12614 listWidth: undefined,
12616 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12617 * mode = 'remote' or 'text' if mode = 'local')
12619 displayField: undefined,
12622 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12623 * mode = 'remote' or 'value' if mode = 'local').
12624 * Note: use of a valueField requires the user make a selection
12625 * in order for a value to be mapped.
12627 valueField: undefined,
12629 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12634 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12635 * field's data value (defaults to the underlying DOM element's name)
12637 hiddenName: undefined,
12639 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12643 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12645 selectedClass: 'active',
12648 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12652 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12653 * anchor positions (defaults to 'tl-bl')
12655 listAlign: 'tl-bl?',
12657 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12661 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
12662 * query specified by the allQuery config option (defaults to 'query')
12664 triggerAction: 'query',
12666 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12667 * (defaults to 4, does not apply if editable = false)
12671 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12672 * delay (typeAheadDelay) if it matches a known value (defaults to false)
12676 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12677 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12681 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12682 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
12686 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
12687 * when editable = true (defaults to false)
12689 selectOnFocus:false,
12691 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12693 queryParam: 'query',
12695 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
12696 * when mode = 'remote' (defaults to 'Loading...')
12698 loadingText: 'Loading...',
12700 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12704 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12708 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12709 * traditional select (defaults to true)
12713 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12717 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12721 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12722 * listWidth has a higher value)
12726 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12727 * allow the user to set arbitrary text into the field (defaults to false)
12729 forceSelection:false,
12731 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12732 * if typeAhead = true (defaults to 250)
12734 typeAheadDelay : 250,
12736 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12737 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12739 valueNotFoundText : undefined,
12741 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12743 blockFocus : false,
12746 * @cfg {Boolean} disableClear Disable showing of clear button.
12748 disableClear : false,
12750 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
12752 alwaysQuery : false,
12755 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
12760 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12762 invalidClass : "has-warning",
12765 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12767 validClass : "has-success",
12770 * @cfg {Boolean} specialFilter (true|false) special filter default false
12772 specialFilter : false,
12775 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12777 mobileTouchView : true,
12780 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12782 useNativeIOS : false,
12784 ios_options : false,
12796 btnPosition : 'right',
12797 triggerList : true,
12798 showToggleBtn : true,
12800 emptyResultText: 'Empty',
12801 triggerText : 'Select',
12804 // element that contains real text value.. (when hidden is used..)
12806 getAutoCreate : function()
12811 * Render classic select for iso
12814 if(Roo.isIOS && this.useNativeIOS){
12815 cfg = this.getAutoCreateNativeIOS();
12823 if(Roo.isTouch && this.mobileTouchView){
12824 cfg = this.getAutoCreateTouchView();
12831 if(!this.tickable){
12832 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12837 * ComboBox with tickable selections
12840 var align = this.labelAlign || this.parentLabelAlign();
12843 cls : 'form-group roo-combobox-tickable' //input-group
12846 var btn_text_select = '';
12847 var btn_text_done = '';
12848 var btn_text_cancel = '';
12850 if (this.btn_text_show) {
12851 btn_text_select = 'Select';
12852 btn_text_done = 'Done';
12853 btn_text_cancel = 'Cancel';
12858 cls : 'tickable-buttons',
12863 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12864 //html : this.triggerText
12865 html: btn_text_select
12871 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12873 html: btn_text_done
12879 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12881 html: btn_text_cancel
12887 buttons.cn.unshift({
12889 cls: 'roo-select2-search-field-input'
12895 Roo.each(buttons.cn, function(c){
12897 c.cls += ' btn-' + _this.size;
12900 if (_this.disabled) {
12911 cls: 'form-hidden-field'
12915 cls: 'roo-select2-choices',
12919 cls: 'roo-select2-search-field',
12930 cls: 'roo-select2-container input-group roo-select2-container-multi',
12935 // cls: 'typeahead typeahead-long dropdown-menu',
12936 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
12941 if(this.hasFeedback && !this.allowBlank){
12945 cls: 'glyphicon form-control-feedback'
12948 combobox.cn.push(feedback);
12952 if (align ==='left' && this.fieldLabel.length) {
12954 cfg.cls += ' roo-form-group-label-left';
12959 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12960 tooltip : 'This field is required'
12965 cls : 'control-label',
12966 html : this.fieldLabel
12978 var labelCfg = cfg.cn[1];
12979 var contentCfg = cfg.cn[2];
12982 if(this.indicatorpos == 'right'){
12988 cls : 'control-label',
12992 html : this.fieldLabel
12996 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12997 tooltip : 'This field is required'
13012 labelCfg = cfg.cn[0];
13013 contentCfg = cfg.cn[1];
13017 if(this.labelWidth > 12){
13018 labelCfg.style = "width: " + this.labelWidth + 'px';
13021 if(this.labelWidth < 13 && this.labelmd == 0){
13022 this.labelmd = this.labelWidth;
13025 if(this.labellg > 0){
13026 labelCfg.cls += ' col-lg-' + this.labellg;
13027 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13030 if(this.labelmd > 0){
13031 labelCfg.cls += ' col-md-' + this.labelmd;
13032 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13035 if(this.labelsm > 0){
13036 labelCfg.cls += ' col-sm-' + this.labelsm;
13037 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13040 if(this.labelxs > 0){
13041 labelCfg.cls += ' col-xs-' + this.labelxs;
13042 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13046 } else if ( this.fieldLabel.length) {
13047 // Roo.log(" label");
13051 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13052 tooltip : 'This field is required'
13056 //cls : 'input-group-addon',
13057 html : this.fieldLabel
13062 if(this.indicatorpos == 'right'){
13066 //cls : 'input-group-addon',
13067 html : this.fieldLabel
13071 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13072 tooltip : 'This field is required'
13081 // Roo.log(" no label && no align");
13088 ['xs','sm','md','lg'].map(function(size){
13089 if (settings[size]) {
13090 cfg.cls += ' col-' + size + '-' + settings[size];
13098 _initEventsCalled : false,
13101 initEvents: function()
13103 if (this._initEventsCalled) { // as we call render... prevent looping...
13106 this._initEventsCalled = true;
13109 throw "can not find store for combo";
13112 this.indicator = this.indicatorEl();
13114 this.store = Roo.factory(this.store, Roo.data);
13115 this.store.parent = this;
13117 // if we are building from html. then this element is so complex, that we can not really
13118 // use the rendered HTML.
13119 // so we have to trash and replace the previous code.
13120 if (Roo.XComponent.build_from_html) {
13121 // remove this element....
13122 var e = this.el.dom, k=0;
13123 while (e ) { e = e.previousSibling; ++k;}
13128 this.rendered = false;
13130 this.render(this.parent().getChildContainer(true), k);
13133 if(Roo.isIOS && this.useNativeIOS){
13134 this.initIOSView();
13142 if(Roo.isTouch && this.mobileTouchView){
13143 this.initTouchView();
13148 this.initTickableEvents();
13152 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13154 if(this.hiddenName){
13156 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13158 this.hiddenField.dom.value =
13159 this.hiddenValue !== undefined ? this.hiddenValue :
13160 this.value !== undefined ? this.value : '';
13162 // prevent input submission
13163 this.el.dom.removeAttribute('name');
13164 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13169 // this.el.dom.setAttribute('autocomplete', 'off');
13172 var cls = 'x-combo-list';
13174 //this.list = new Roo.Layer({
13175 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13181 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13182 _this.list.setWidth(lw);
13185 this.list.on('mouseover', this.onViewOver, this);
13186 this.list.on('mousemove', this.onViewMove, this);
13187 this.list.on('scroll', this.onViewScroll, this);
13190 this.list.swallowEvent('mousewheel');
13191 this.assetHeight = 0;
13194 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13195 this.assetHeight += this.header.getHeight();
13198 this.innerList = this.list.createChild({cls:cls+'-inner'});
13199 this.innerList.on('mouseover', this.onViewOver, this);
13200 this.innerList.on('mousemove', this.onViewMove, this);
13201 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13203 if(this.allowBlank && !this.pageSize && !this.disableClear){
13204 this.footer = this.list.createChild({cls:cls+'-ft'});
13205 this.pageTb = new Roo.Toolbar(this.footer);
13209 this.footer = this.list.createChild({cls:cls+'-ft'});
13210 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13211 {pageSize: this.pageSize});
13215 if (this.pageTb && this.allowBlank && !this.disableClear) {
13217 this.pageTb.add(new Roo.Toolbar.Fill(), {
13218 cls: 'x-btn-icon x-btn-clear',
13220 handler: function()
13223 _this.clearValue();
13224 _this.onSelect(false, -1);
13229 this.assetHeight += this.footer.getHeight();
13234 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13237 this.view = new Roo.View(this.list, this.tpl, {
13238 singleSelect:true, store: this.store, selectedClass: this.selectedClass
13240 //this.view.wrapEl.setDisplayed(false);
13241 this.view.on('click', this.onViewClick, this);
13244 this.store.on('beforeload', this.onBeforeLoad, this);
13245 this.store.on('load', this.onLoad, this);
13246 this.store.on('loadexception', this.onLoadException, this);
13248 if(this.resizable){
13249 this.resizer = new Roo.Resizable(this.list, {
13250 pinned:true, handles:'se'
13252 this.resizer.on('resize', function(r, w, h){
13253 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13254 this.listWidth = w;
13255 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13256 this.restrictHeight();
13258 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13261 if(!this.editable){
13262 this.editable = true;
13263 this.setEditable(false);
13268 if (typeof(this.events.add.listeners) != 'undefined') {
13270 this.addicon = this.wrap.createChild(
13271 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
13273 this.addicon.on('click', function(e) {
13274 this.fireEvent('add', this);
13277 if (typeof(this.events.edit.listeners) != 'undefined') {
13279 this.editicon = this.wrap.createChild(
13280 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
13281 if (this.addicon) {
13282 this.editicon.setStyle('margin-left', '40px');
13284 this.editicon.on('click', function(e) {
13286 // we fire even if inothing is selected..
13287 this.fireEvent('edit', this, this.lastData );
13293 this.keyNav = new Roo.KeyNav(this.inputEl(), {
13294 "up" : function(e){
13295 this.inKeyMode = true;
13299 "down" : function(e){
13300 if(!this.isExpanded()){
13301 this.onTriggerClick();
13303 this.inKeyMode = true;
13308 "enter" : function(e){
13309 // this.onViewClick();
13313 if(this.fireEvent("specialkey", this, e)){
13314 this.onViewClick(false);
13320 "esc" : function(e){
13324 "tab" : function(e){
13327 if(this.fireEvent("specialkey", this, e)){
13328 this.onViewClick(false);
13336 doRelay : function(foo, bar, hname){
13337 if(hname == 'down' || this.scope.isExpanded()){
13338 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13347 this.queryDelay = Math.max(this.queryDelay || 10,
13348 this.mode == 'local' ? 10 : 250);
13351 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13353 if(this.typeAhead){
13354 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13356 if(this.editable !== false){
13357 this.inputEl().on("keyup", this.onKeyUp, this);
13359 if(this.forceSelection){
13360 this.inputEl().on('blur', this.doForce, this);
13364 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13365 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13369 initTickableEvents: function()
13373 if(this.hiddenName){
13375 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13377 this.hiddenField.dom.value =
13378 this.hiddenValue !== undefined ? this.hiddenValue :
13379 this.value !== undefined ? this.value : '';
13381 // prevent input submission
13382 this.el.dom.removeAttribute('name');
13383 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13388 // this.list = this.el.select('ul.dropdown-menu',true).first();
13390 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13391 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13392 if(this.triggerList){
13393 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13396 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13397 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13399 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13400 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13402 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13403 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13405 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13406 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13407 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13410 this.cancelBtn.hide();
13415 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13416 _this.list.setWidth(lw);
13419 this.list.on('mouseover', this.onViewOver, this);
13420 this.list.on('mousemove', this.onViewMove, this);
13422 this.list.on('scroll', this.onViewScroll, this);
13425 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}" type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13428 this.view = new Roo.View(this.list, this.tpl, {
13429 singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
13432 //this.view.wrapEl.setDisplayed(false);
13433 this.view.on('click', this.onViewClick, this);
13437 this.store.on('beforeload', this.onBeforeLoad, this);
13438 this.store.on('load', this.onLoad, this);
13439 this.store.on('loadexception', this.onLoadException, this);
13442 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13443 "up" : function(e){
13444 this.inKeyMode = true;
13448 "down" : function(e){
13449 this.inKeyMode = true;
13453 "enter" : function(e){
13454 if(this.fireEvent("specialkey", this, e)){
13455 this.onViewClick(false);
13461 "esc" : function(e){
13462 this.onTickableFooterButtonClick(e, false, false);
13465 "tab" : function(e){
13466 this.fireEvent("specialkey", this, e);
13468 this.onTickableFooterButtonClick(e, false, false);
13475 doRelay : function(e, fn, key){
13476 if(this.scope.isExpanded()){
13477 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13486 this.queryDelay = Math.max(this.queryDelay || 10,
13487 this.mode == 'local' ? 10 : 250);
13490 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13492 if(this.typeAhead){
13493 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13496 if(this.editable !== false){
13497 this.tickableInputEl().on("keyup", this.onKeyUp, this);
13500 this.indicator = this.indicatorEl();
13502 if(this.indicator){
13503 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13504 this.indicator.hide();
13509 onDestroy : function(){
13511 this.view.setStore(null);
13512 this.view.el.removeAllListeners();
13513 this.view.el.remove();
13514 this.view.purgeListeners();
13517 this.list.dom.innerHTML = '';
13521 this.store.un('beforeload', this.onBeforeLoad, this);
13522 this.store.un('load', this.onLoad, this);
13523 this.store.un('loadexception', this.onLoadException, this);
13525 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13529 fireKey : function(e){
13530 if(e.isNavKeyPress() && !this.list.isVisible()){
13531 this.fireEvent("specialkey", this, e);
13536 onResize: function(w, h){
13537 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13539 // if(typeof w != 'number'){
13540 // // we do not handle it!?!?
13543 // var tw = this.trigger.getWidth();
13544 // // tw += this.addicon ? this.addicon.getWidth() : 0;
13545 // // tw += this.editicon ? this.editicon.getWidth() : 0;
13547 // this.inputEl().setWidth( this.adjustWidth('input', x));
13549 // //this.trigger.setStyle('left', x+'px');
13551 // if(this.list && this.listWidth === undefined){
13552 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13553 // this.list.setWidth(lw);
13554 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13562 * Allow or prevent the user from directly editing the field text. If false is passed,
13563 * the user will only be able to select from the items defined in the dropdown list. This method
13564 * is the runtime equivalent of setting the 'editable' config option at config time.
13565 * @param {Boolean} value True to allow the user to directly edit the field text
13567 setEditable : function(value){
13568 if(value == this.editable){
13571 this.editable = value;
13573 this.inputEl().dom.setAttribute('readOnly', true);
13574 this.inputEl().on('mousedown', this.onTriggerClick, this);
13575 this.inputEl().addClass('x-combo-noedit');
13577 this.inputEl().dom.setAttribute('readOnly', false);
13578 this.inputEl().un('mousedown', this.onTriggerClick, this);
13579 this.inputEl().removeClass('x-combo-noedit');
13585 onBeforeLoad : function(combo,opts){
13586 if(!this.hasFocus){
13590 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13592 this.restrictHeight();
13593 this.selectedIndex = -1;
13597 onLoad : function(){
13599 this.hasQuery = false;
13601 if(!this.hasFocus){
13605 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13606 this.loading.hide();
13609 if(this.store.getCount() > 0){
13612 this.restrictHeight();
13613 if(this.lastQuery == this.allQuery){
13614 if(this.editable && !this.tickable){
13615 this.inputEl().dom.select();
13619 !this.selectByValue(this.value, true) &&
13622 !this.store.lastOptions ||
13623 typeof(this.store.lastOptions.add) == 'undefined' ||
13624 this.store.lastOptions.add != true
13627 this.select(0, true);
13630 if(this.autoFocus){
13633 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13634 this.taTask.delay(this.typeAheadDelay);
13638 this.onEmptyResults();
13644 onLoadException : function()
13646 this.hasQuery = false;
13648 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13649 this.loading.hide();
13652 if(this.tickable && this.editable){
13657 // only causes errors at present
13658 //Roo.log(this.store.reader.jsonData);
13659 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13661 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13667 onTypeAhead : function(){
13668 if(this.store.getCount() > 0){
13669 var r = this.store.getAt(0);
13670 var newValue = r.data[this.displayField];
13671 var len = newValue.length;
13672 var selStart = this.getRawValue().length;
13674 if(selStart != len){
13675 this.setRawValue(newValue);
13676 this.selectText(selStart, newValue.length);
13682 onSelect : function(record, index){
13684 if(this.fireEvent('beforeselect', this, record, index) !== false){
13686 this.setFromData(index > -1 ? record.data : false);
13689 this.fireEvent('select', this, record, index);
13694 * Returns the currently selected field value or empty string if no value is set.
13695 * @return {String} value The selected value
13697 getValue : function()
13699 if(Roo.isIOS && this.useNativeIOS){
13700 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13704 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13707 if(this.valueField){
13708 return typeof this.value != 'undefined' ? this.value : '';
13710 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13714 getRawValue : function()
13716 if(Roo.isIOS && this.useNativeIOS){
13717 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13720 var v = this.inputEl().getValue();
13726 * Clears any text/value currently set in the field
13728 clearValue : function(){
13730 if(this.hiddenField){
13731 this.hiddenField.dom.value = '';
13734 this.setRawValue('');
13735 this.lastSelectionText = '';
13736 this.lastData = false;
13738 var close = this.closeTriggerEl();
13749 * Sets the specified value into the field. If the value finds a match, the corresponding record text
13750 * will be displayed in the field. If the value does not match the data value of an existing item,
13751 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13752 * Otherwise the field will be blank (although the value will still be set).
13753 * @param {String} value The value to match
13755 setValue : function(v)
13757 if(Roo.isIOS && this.useNativeIOS){
13758 this.setIOSValue(v);
13768 if(this.valueField){
13769 var r = this.findRecord(this.valueField, v);
13771 text = r.data[this.displayField];
13772 }else if(this.valueNotFoundText !== undefined){
13773 text = this.valueNotFoundText;
13776 this.lastSelectionText = text;
13777 if(this.hiddenField){
13778 this.hiddenField.dom.value = v;
13780 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13783 var close = this.closeTriggerEl();
13786 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13792 * @property {Object} the last set data for the element
13797 * Sets the value of the field based on a object which is related to the record format for the store.
13798 * @param {Object} value the value to set as. or false on reset?
13800 setFromData : function(o){
13807 var dv = ''; // display value
13808 var vv = ''; // value value..
13810 if (this.displayField) {
13811 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13813 // this is an error condition!!!
13814 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
13817 if(this.valueField){
13818 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13821 var close = this.closeTriggerEl();
13824 if(dv.length || vv * 1 > 0){
13826 this.blockFocus=true;
13832 if(this.hiddenField){
13833 this.hiddenField.dom.value = vv;
13835 this.lastSelectionText = dv;
13836 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13840 // no hidden field.. - we store the value in 'value', but still display
13841 // display field!!!!
13842 this.lastSelectionText = dv;
13843 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13850 reset : function(){
13851 // overridden so that last data is reset..
13858 this.setValue(this.originalValue);
13859 //this.clearInvalid();
13860 this.lastData = false;
13862 this.view.clearSelections();
13868 findRecord : function(prop, value){
13870 if(this.store.getCount() > 0){
13871 this.store.each(function(r){
13872 if(r.data[prop] == value){
13882 getName: function()
13884 // returns hidden if it's set..
13885 if (!this.rendered) {return ''};
13886 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
13890 onViewMove : function(e, t){
13891 this.inKeyMode = false;
13895 onViewOver : function(e, t){
13896 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13899 var item = this.view.findItemFromChild(t);
13902 var index = this.view.indexOf(item);
13903 this.select(index, false);
13908 onViewClick : function(view, doFocus, el, e)
13910 var index = this.view.getSelectedIndexes()[0];
13912 var r = this.store.getAt(index);
13916 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13923 Roo.each(this.tickItems, function(v,k){
13925 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13927 _this.tickItems.splice(k, 1);
13929 if(typeof(e) == 'undefined' && view == false){
13930 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13942 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13943 this.tickItems.push(r.data);
13946 if(typeof(e) == 'undefined' && view == false){
13947 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13954 this.onSelect(r, index);
13956 if(doFocus !== false && !this.blockFocus){
13957 this.inputEl().focus();
13962 restrictHeight : function(){
13963 //this.innerList.dom.style.height = '';
13964 //var inner = this.innerList.dom;
13965 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13966 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13967 //this.list.beginUpdate();
13968 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13969 this.list.alignTo(this.inputEl(), this.listAlign);
13970 this.list.alignTo(this.inputEl(), this.listAlign);
13971 //this.list.endUpdate();
13975 onEmptyResults : function(){
13977 if(this.tickable && this.editable){
13978 this.restrictHeight();
13986 * Returns true if the dropdown list is expanded, else false.
13988 isExpanded : function(){
13989 return this.list.isVisible();
13993 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13994 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13995 * @param {String} value The data value of the item to select
13996 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13997 * selected item if it is not currently in view (defaults to true)
13998 * @return {Boolean} True if the value matched an item in the list, else false
14000 selectByValue : function(v, scrollIntoView){
14001 if(v !== undefined && v !== null){
14002 var r = this.findRecord(this.valueField || this.displayField, v);
14004 this.select(this.store.indexOf(r), scrollIntoView);
14012 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14013 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14014 * @param {Number} index The zero-based index of the list item to select
14015 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14016 * selected item if it is not currently in view (defaults to true)
14018 select : function(index, scrollIntoView){
14019 this.selectedIndex = index;
14020 this.view.select(index);
14021 if(scrollIntoView !== false){
14022 var el = this.view.getNode(index);
14024 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14027 this.list.scrollChildIntoView(el, false);
14033 selectNext : function(){
14034 var ct = this.store.getCount();
14036 if(this.selectedIndex == -1){
14038 }else if(this.selectedIndex < ct-1){
14039 this.select(this.selectedIndex+1);
14045 selectPrev : function(){
14046 var ct = this.store.getCount();
14048 if(this.selectedIndex == -1){
14050 }else if(this.selectedIndex != 0){
14051 this.select(this.selectedIndex-1);
14057 onKeyUp : function(e){
14058 if(this.editable !== false && !e.isSpecialKey()){
14059 this.lastKey = e.getKey();
14060 this.dqTask.delay(this.queryDelay);
14065 validateBlur : function(){
14066 return !this.list || !this.list.isVisible();
14070 initQuery : function(){
14072 var v = this.getRawValue();
14074 if(this.tickable && this.editable){
14075 v = this.tickableInputEl().getValue();
14082 doForce : function(){
14083 if(this.inputEl().dom.value.length > 0){
14084 this.inputEl().dom.value =
14085 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14091 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
14092 * query allowing the query action to be canceled if needed.
14093 * @param {String} query The SQL query to execute
14094 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14095 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
14096 * saved in the current store (defaults to false)
14098 doQuery : function(q, forceAll){
14100 if(q === undefined || q === null){
14105 forceAll: forceAll,
14109 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14114 forceAll = qe.forceAll;
14115 if(forceAll === true || (q.length >= this.minChars)){
14117 this.hasQuery = true;
14119 if(this.lastQuery != q || this.alwaysQuery){
14120 this.lastQuery = q;
14121 if(this.mode == 'local'){
14122 this.selectedIndex = -1;
14124 this.store.clearFilter();
14127 if(this.specialFilter){
14128 this.fireEvent('specialfilter', this);
14133 this.store.filter(this.displayField, q);
14136 this.store.fireEvent("datachanged", this.store);
14143 this.store.baseParams[this.queryParam] = q;
14145 var options = {params : this.getParams(q)};
14148 options.add = true;
14149 options.params.start = this.page * this.pageSize;
14152 this.store.load(options);
14155 * this code will make the page width larger, at the beginning, the list not align correctly,
14156 * we should expand the list on onLoad
14157 * so command out it
14162 this.selectedIndex = -1;
14167 this.loadNext = false;
14171 getParams : function(q){
14173 //p[this.queryParam] = q;
14177 p.limit = this.pageSize;
14183 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14185 collapse : function(){
14186 if(!this.isExpanded()){
14192 this.hasFocus = false;
14196 this.cancelBtn.hide();
14197 this.trigger.show();
14200 this.tickableInputEl().dom.value = '';
14201 this.tickableInputEl().blur();
14206 Roo.get(document).un('mousedown', this.collapseIf, this);
14207 Roo.get(document).un('mousewheel', this.collapseIf, this);
14208 if (!this.editable) {
14209 Roo.get(document).un('keydown', this.listKeyPress, this);
14211 this.fireEvent('collapse', this);
14217 collapseIf : function(e){
14218 var in_combo = e.within(this.el);
14219 var in_list = e.within(this.list);
14220 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14222 if (in_combo || in_list || is_list) {
14223 //e.stopPropagation();
14228 this.onTickableFooterButtonClick(e, false, false);
14236 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14238 expand : function(){
14240 if(this.isExpanded() || !this.hasFocus){
14244 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14245 this.list.setWidth(lw);
14251 this.restrictHeight();
14255 this.tickItems = Roo.apply([], this.item);
14258 this.cancelBtn.show();
14259 this.trigger.hide();
14262 this.tickableInputEl().focus();
14267 Roo.get(document).on('mousedown', this.collapseIf, this);
14268 Roo.get(document).on('mousewheel', this.collapseIf, this);
14269 if (!this.editable) {
14270 Roo.get(document).on('keydown', this.listKeyPress, this);
14273 this.fireEvent('expand', this);
14277 // Implements the default empty TriggerField.onTriggerClick function
14278 onTriggerClick : function(e)
14280 Roo.log('trigger click');
14282 if(this.disabled || !this.triggerList){
14287 this.loadNext = false;
14289 if(this.isExpanded()){
14291 if (!this.blockFocus) {
14292 this.inputEl().focus();
14296 this.hasFocus = true;
14297 if(this.triggerAction == 'all') {
14298 this.doQuery(this.allQuery, true);
14300 this.doQuery(this.getRawValue());
14302 if (!this.blockFocus) {
14303 this.inputEl().focus();
14308 onTickableTriggerClick : function(e)
14315 this.loadNext = false;
14316 this.hasFocus = true;
14318 if(this.triggerAction == 'all') {
14319 this.doQuery(this.allQuery, true);
14321 this.doQuery(this.getRawValue());
14325 onSearchFieldClick : function(e)
14327 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14328 this.onTickableFooterButtonClick(e, false, false);
14332 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14337 this.loadNext = false;
14338 this.hasFocus = true;
14340 if(this.triggerAction == 'all') {
14341 this.doQuery(this.allQuery, true);
14343 this.doQuery(this.getRawValue());
14347 listKeyPress : function(e)
14349 //Roo.log('listkeypress');
14350 // scroll to first matching element based on key pres..
14351 if (e.isSpecialKey()) {
14354 var k = String.fromCharCode(e.getKey()).toUpperCase();
14357 var csel = this.view.getSelectedNodes();
14358 var cselitem = false;
14360 var ix = this.view.indexOf(csel[0]);
14361 cselitem = this.store.getAt(ix);
14362 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14368 this.store.each(function(v) {
14370 // start at existing selection.
14371 if (cselitem.id == v.id) {
14377 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14378 match = this.store.indexOf(v);
14384 if (match === false) {
14385 return true; // no more action?
14388 this.view.select(match);
14389 var sn = Roo.get(this.view.getSelectedNodes()[0]);
14390 sn.scrollIntoView(sn.dom.parentNode, false);
14393 onViewScroll : function(e, t){
14395 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){
14399 this.hasQuery = true;
14401 this.loading = this.list.select('.loading', true).first();
14403 if(this.loading === null){
14404 this.list.createChild({
14406 cls: 'loading roo-select2-more-results roo-select2-active',
14407 html: 'Loading more results...'
14410 this.loading = this.list.select('.loading', true).first();
14412 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14414 this.loading.hide();
14417 this.loading.show();
14422 this.loadNext = true;
14424 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14429 addItem : function(o)
14431 var dv = ''; // display value
14433 if (this.displayField) {
14434 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14436 // this is an error condition!!!
14437 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14444 var choice = this.choices.createChild({
14446 cls: 'roo-select2-search-choice',
14455 cls: 'roo-select2-search-choice-close fa fa-times',
14460 }, this.searchField);
14462 var close = choice.select('a.roo-select2-search-choice-close', true).first();
14464 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14472 this.inputEl().dom.value = '';
14477 onRemoveItem : function(e, _self, o)
14479 e.preventDefault();
14481 this.lastItem = Roo.apply([], this.item);
14483 var index = this.item.indexOf(o.data) * 1;
14486 Roo.log('not this item?!');
14490 this.item.splice(index, 1);
14495 this.fireEvent('remove', this, e);
14501 syncValue : function()
14503 if(!this.item.length){
14510 Roo.each(this.item, function(i){
14511 if(_this.valueField){
14512 value.push(i[_this.valueField]);
14519 this.value = value.join(',');
14521 if(this.hiddenField){
14522 this.hiddenField.dom.value = this.value;
14525 this.store.fireEvent("datachanged", this.store);
14530 clearItem : function()
14532 if(!this.multiple){
14538 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14546 if(this.tickable && !Roo.isTouch){
14547 this.view.refresh();
14551 inputEl: function ()
14553 if(Roo.isIOS && this.useNativeIOS){
14554 return this.el.select('select.roo-ios-select', true).first();
14557 if(Roo.isTouch && this.mobileTouchView){
14558 return this.el.select('input.form-control',true).first();
14562 return this.searchField;
14565 return this.el.select('input.form-control',true).first();
14568 onTickableFooterButtonClick : function(e, btn, el)
14570 e.preventDefault();
14572 this.lastItem = Roo.apply([], this.item);
14574 if(btn && btn.name == 'cancel'){
14575 this.tickItems = Roo.apply([], this.item);
14584 Roo.each(this.tickItems, function(o){
14592 validate : function()
14594 var v = this.getRawValue();
14597 v = this.getValue();
14600 if(this.disabled || this.allowBlank || v.length){
14605 this.markInvalid();
14609 tickableInputEl : function()
14611 if(!this.tickable || !this.editable){
14612 return this.inputEl();
14615 return this.inputEl().select('.roo-select2-search-field-input', true).first();
14619 getAutoCreateTouchView : function()
14624 cls: 'form-group' //input-group
14630 type : this.inputType,
14631 cls : 'form-control x-combo-noedit',
14632 autocomplete: 'new-password',
14633 placeholder : this.placeholder || '',
14638 input.name = this.name;
14642 input.cls += ' input-' + this.size;
14645 if (this.disabled) {
14646 input.disabled = true;
14657 inputblock.cls += ' input-group';
14659 inputblock.cn.unshift({
14661 cls : 'input-group-addon',
14666 if(this.removable && !this.multiple){
14667 inputblock.cls += ' roo-removable';
14669 inputblock.cn.push({
14672 cls : 'roo-combo-removable-btn close'
14676 if(this.hasFeedback && !this.allowBlank){
14678 inputblock.cls += ' has-feedback';
14680 inputblock.cn.push({
14682 cls: 'glyphicon form-control-feedback'
14689 inputblock.cls += (this.before) ? '' : ' input-group';
14691 inputblock.cn.push({
14693 cls : 'input-group-addon',
14704 cls: 'form-hidden-field'
14718 cls: 'form-hidden-field'
14722 cls: 'roo-select2-choices',
14726 cls: 'roo-select2-search-field',
14739 cls: 'roo-select2-container input-group roo-touchview-combobox ',
14745 if(!this.multiple && this.showToggleBtn){
14752 if (this.caret != false) {
14755 cls: 'fa fa-' + this.caret
14762 cls : 'input-group-addon btn dropdown-toggle',
14767 cls: 'combobox-clear',
14781 combobox.cls += ' roo-select2-container-multi';
14784 var align = this.labelAlign || this.parentLabelAlign();
14786 if (align ==='left' && this.fieldLabel.length) {
14791 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14792 tooltip : 'This field is required'
14796 cls : 'control-label',
14797 html : this.fieldLabel
14808 var labelCfg = cfg.cn[1];
14809 var contentCfg = cfg.cn[2];
14812 if(this.indicatorpos == 'right'){
14817 cls : 'control-label',
14821 html : this.fieldLabel
14825 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14826 tooltip : 'This field is required'
14839 labelCfg = cfg.cn[0];
14840 contentCfg = cfg.cn[1];
14845 if(this.labelWidth > 12){
14846 labelCfg.style = "width: " + this.labelWidth + 'px';
14849 if(this.labelWidth < 13 && this.labelmd == 0){
14850 this.labelmd = this.labelWidth;
14853 if(this.labellg > 0){
14854 labelCfg.cls += ' col-lg-' + this.labellg;
14855 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14858 if(this.labelmd > 0){
14859 labelCfg.cls += ' col-md-' + this.labelmd;
14860 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14863 if(this.labelsm > 0){
14864 labelCfg.cls += ' col-sm-' + this.labelsm;
14865 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14868 if(this.labelxs > 0){
14869 labelCfg.cls += ' col-xs-' + this.labelxs;
14870 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14874 } else if ( this.fieldLabel.length) {
14878 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14879 tooltip : 'This field is required'
14883 cls : 'control-label',
14884 html : this.fieldLabel
14895 if(this.indicatorpos == 'right'){
14899 cls : 'control-label',
14900 html : this.fieldLabel,
14904 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14905 tooltip : 'This field is required'
14922 var settings = this;
14924 ['xs','sm','md','lg'].map(function(size){
14925 if (settings[size]) {
14926 cfg.cls += ' col-' + size + '-' + settings[size];
14933 initTouchView : function()
14935 this.renderTouchView();
14937 this.touchViewEl.on('scroll', function(){
14938 this.el.dom.scrollTop = 0;
14941 this.originalValue = this.getValue();
14943 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14945 this.inputEl().on("click", this.showTouchView, this);
14946 if (this.triggerEl) {
14947 this.triggerEl.on("click", this.showTouchView, this);
14951 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14952 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14954 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14956 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14957 this.store.on('load', this.onTouchViewLoad, this);
14958 this.store.on('loadexception', this.onTouchViewLoadException, this);
14960 if(this.hiddenName){
14962 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14964 this.hiddenField.dom.value =
14965 this.hiddenValue !== undefined ? this.hiddenValue :
14966 this.value !== undefined ? this.value : '';
14968 this.el.dom.removeAttribute('name');
14969 this.hiddenField.dom.setAttribute('name', this.hiddenName);
14973 this.choices = this.el.select('ul.roo-select2-choices', true).first();
14974 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14977 if(this.removable && !this.multiple){
14978 var close = this.closeTriggerEl();
14980 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14981 close.on('click', this.removeBtnClick, this, close);
14985 * fix the bug in Safari iOS8
14987 this.inputEl().on("focus", function(e){
14988 document.activeElement.blur();
14996 renderTouchView : function()
14998 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14999 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15001 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15002 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15004 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15005 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15006 this.touchViewBodyEl.setStyle('overflow', 'auto');
15008 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15009 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15011 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15012 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15016 showTouchView : function()
15022 this.touchViewHeaderEl.hide();
15024 if(this.modalTitle.length){
15025 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15026 this.touchViewHeaderEl.show();
15029 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15030 this.touchViewEl.show();
15032 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15034 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15035 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15037 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15039 if(this.modalTitle.length){
15040 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15043 this.touchViewBodyEl.setHeight(bodyHeight);
15047 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15049 this.touchViewEl.addClass('in');
15052 this.doTouchViewQuery();
15056 hideTouchView : function()
15058 this.touchViewEl.removeClass('in');
15062 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15064 this.touchViewEl.setStyle('display', 'none');
15069 setTouchViewValue : function()
15076 Roo.each(this.tickItems, function(o){
15081 this.hideTouchView();
15084 doTouchViewQuery : function()
15093 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15097 if(!this.alwaysQuery || this.mode == 'local'){
15098 this.onTouchViewLoad();
15105 onTouchViewBeforeLoad : function(combo,opts)
15111 onTouchViewLoad : function()
15113 if(this.store.getCount() < 1){
15114 this.onTouchViewEmptyResults();
15118 this.clearTouchView();
15120 var rawValue = this.getRawValue();
15122 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15124 this.tickItems = [];
15126 this.store.data.each(function(d, rowIndex){
15127 var row = this.touchViewListGroup.createChild(template);
15129 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15130 row.addClass(d.data.cls);
15133 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15136 html : d.data[this.displayField]
15139 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15140 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15143 row.removeClass('selected');
15144 if(!this.multiple && this.valueField &&
15145 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15148 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15149 row.addClass('selected');
15152 if(this.multiple && this.valueField &&
15153 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15157 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15158 this.tickItems.push(d.data);
15161 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15165 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15167 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15169 if(this.modalTitle.length){
15170 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15173 var listHeight = this.touchViewListGroup.getHeight();
15177 if(firstChecked && listHeight > bodyHeight){
15178 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15183 onTouchViewLoadException : function()
15185 this.hideTouchView();
15188 onTouchViewEmptyResults : function()
15190 this.clearTouchView();
15192 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15194 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15198 clearTouchView : function()
15200 this.touchViewListGroup.dom.innerHTML = '';
15203 onTouchViewClick : function(e, el, o)
15205 e.preventDefault();
15208 var rowIndex = o.rowIndex;
15210 var r = this.store.getAt(rowIndex);
15212 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15214 if(!this.multiple){
15215 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15216 c.dom.removeAttribute('checked');
15219 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15221 this.setFromData(r.data);
15223 var close = this.closeTriggerEl();
15229 this.hideTouchView();
15231 this.fireEvent('select', this, r, rowIndex);
15236 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15237 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15238 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15242 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15243 this.addItem(r.data);
15244 this.tickItems.push(r.data);
15248 getAutoCreateNativeIOS : function()
15251 cls: 'form-group' //input-group,
15256 cls : 'roo-ios-select'
15260 combobox.name = this.name;
15263 if (this.disabled) {
15264 combobox.disabled = true;
15267 var settings = this;
15269 ['xs','sm','md','lg'].map(function(size){
15270 if (settings[size]) {
15271 cfg.cls += ' col-' + size + '-' + settings[size];
15281 initIOSView : function()
15283 this.store.on('load', this.onIOSViewLoad, this);
15288 onIOSViewLoad : function()
15290 if(this.store.getCount() < 1){
15294 this.clearIOSView();
15296 if(this.allowBlank) {
15298 var default_text = '-- SELECT --';
15300 if(this.placeholder.length){
15301 default_text = this.placeholder;
15304 if(this.emptyTitle.length){
15305 default_text += ' - ' + this.emptyTitle + ' -';
15308 var opt = this.inputEl().createChild({
15311 html : default_text
15315 o[this.valueField] = 0;
15316 o[this.displayField] = default_text;
15318 this.ios_options.push({
15325 this.store.data.each(function(d, rowIndex){
15329 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15330 html = d.data[this.displayField];
15335 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15336 value = d.data[this.valueField];
15345 if(this.value == d.data[this.valueField]){
15346 option['selected'] = true;
15349 var opt = this.inputEl().createChild(option);
15351 this.ios_options.push({
15358 this.inputEl().on('change', function(){
15359 this.fireEvent('select', this);
15364 clearIOSView: function()
15366 this.inputEl().dom.innerHTML = '';
15368 this.ios_options = [];
15371 setIOSValue: function(v)
15375 if(!this.ios_options){
15379 Roo.each(this.ios_options, function(opts){
15381 opts.el.dom.removeAttribute('selected');
15383 if(opts.data[this.valueField] != v){
15387 opts.el.dom.setAttribute('selected', true);
15393 * @cfg {Boolean} grow
15397 * @cfg {Number} growMin
15401 * @cfg {Number} growMax
15410 Roo.apply(Roo.bootstrap.ComboBox, {
15414 cls: 'modal-header',
15436 cls: 'list-group-item',
15440 cls: 'roo-combobox-list-group-item-value'
15444 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15458 listItemCheckbox : {
15460 cls: 'list-group-item',
15464 cls: 'roo-combobox-list-group-item-value'
15468 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15484 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15489 cls: 'modal-footer',
15497 cls: 'col-xs-6 text-left',
15500 cls: 'btn btn-danger roo-touch-view-cancel',
15506 cls: 'col-xs-6 text-right',
15509 cls: 'btn btn-success roo-touch-view-ok',
15520 Roo.apply(Roo.bootstrap.ComboBox, {
15522 touchViewTemplate : {
15524 cls: 'modal fade roo-combobox-touch-view',
15528 cls: 'modal-dialog',
15529 style : 'position:fixed', // we have to fix position....
15533 cls: 'modal-content',
15535 Roo.bootstrap.ComboBox.header,
15536 Roo.bootstrap.ComboBox.body,
15537 Roo.bootstrap.ComboBox.footer
15546 * Ext JS Library 1.1.1
15547 * Copyright(c) 2006-2007, Ext JS, LLC.
15549 * Originally Released Under LGPL - original licence link has changed is not relivant.
15552 * <script type="text/javascript">
15557 * @extends Roo.util.Observable
15558 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
15559 * This class also supports single and multi selection modes. <br>
15560 * Create a data model bound view:
15562 var store = new Roo.data.Store(...);
15564 var view = new Roo.View({
15566 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
15568 singleSelect: true,
15569 selectedClass: "ydataview-selected",
15573 // listen for node click?
15574 view.on("click", function(vw, index, node, e){
15575 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15579 dataModel.load("foobar.xml");
15581 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15583 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15584 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15586 * Note: old style constructor is still suported (container, template, config)
15589 * Create a new View
15590 * @param {Object} config The config object
15593 Roo.View = function(config, depreciated_tpl, depreciated_config){
15595 this.parent = false;
15597 if (typeof(depreciated_tpl) == 'undefined') {
15598 // new way.. - universal constructor.
15599 Roo.apply(this, config);
15600 this.el = Roo.get(this.el);
15603 this.el = Roo.get(config);
15604 this.tpl = depreciated_tpl;
15605 Roo.apply(this, depreciated_config);
15607 this.wrapEl = this.el.wrap().wrap();
15608 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15611 if(typeof(this.tpl) == "string"){
15612 this.tpl = new Roo.Template(this.tpl);
15614 // support xtype ctors..
15615 this.tpl = new Roo.factory(this.tpl, Roo);
15619 this.tpl.compile();
15624 * @event beforeclick
15625 * Fires before a click is processed. Returns false to cancel the default action.
15626 * @param {Roo.View} this
15627 * @param {Number} index The index of the target node
15628 * @param {HTMLElement} node The target node
15629 * @param {Roo.EventObject} e The raw event object
15631 "beforeclick" : true,
15634 * Fires when a template node is clicked.
15635 * @param {Roo.View} this
15636 * @param {Number} index The index of the target node
15637 * @param {HTMLElement} node The target node
15638 * @param {Roo.EventObject} e The raw event object
15643 * Fires when a template node is double clicked.
15644 * @param {Roo.View} this
15645 * @param {Number} index The index of the target node
15646 * @param {HTMLElement} node The target node
15647 * @param {Roo.EventObject} e The raw event object
15651 * @event contextmenu
15652 * Fires when a template node is right clicked.
15653 * @param {Roo.View} this
15654 * @param {Number} index The index of the target node
15655 * @param {HTMLElement} node The target node
15656 * @param {Roo.EventObject} e The raw event object
15658 "contextmenu" : true,
15660 * @event selectionchange
15661 * Fires when the selected nodes change.
15662 * @param {Roo.View} this
15663 * @param {Array} selections Array of the selected nodes
15665 "selectionchange" : true,
15668 * @event beforeselect
15669 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15670 * @param {Roo.View} this
15671 * @param {HTMLElement} node The node to be selected
15672 * @param {Array} selections Array of currently selected nodes
15674 "beforeselect" : true,
15676 * @event preparedata
15677 * Fires on every row to render, to allow you to change the data.
15678 * @param {Roo.View} this
15679 * @param {Object} data to be rendered (change this)
15681 "preparedata" : true
15689 "click": this.onClick,
15690 "dblclick": this.onDblClick,
15691 "contextmenu": this.onContextMenu,
15695 this.selections = [];
15697 this.cmp = new Roo.CompositeElementLite([]);
15699 this.store = Roo.factory(this.store, Roo.data);
15700 this.setStore(this.store, true);
15703 if ( this.footer && this.footer.xtype) {
15705 var fctr = this.wrapEl.appendChild(document.createElement("div"));
15707 this.footer.dataSource = this.store;
15708 this.footer.container = fctr;
15709 this.footer = Roo.factory(this.footer, Roo);
15710 fctr.insertFirst(this.el);
15712 // this is a bit insane - as the paging toolbar seems to detach the el..
15713 // dom.parentNode.parentNode.parentNode
15714 // they get detached?
15718 Roo.View.superclass.constructor.call(this);
15723 Roo.extend(Roo.View, Roo.util.Observable, {
15726 * @cfg {Roo.data.Store} store Data store to load data from.
15731 * @cfg {String|Roo.Element} el The container element.
15736 * @cfg {String|Roo.Template} tpl The template used by this View
15740 * @cfg {String} dataName the named area of the template to use as the data area
15741 * Works with domtemplates roo-name="name"
15745 * @cfg {String} selectedClass The css class to add to selected nodes
15747 selectedClass : "x-view-selected",
15749 * @cfg {String} emptyText The empty text to show when nothing is loaded.
15754 * @cfg {String} text to display on mask (default Loading)
15758 * @cfg {Boolean} multiSelect Allow multiple selection
15760 multiSelect : false,
15762 * @cfg {Boolean} singleSelect Allow single selection
15764 singleSelect: false,
15767 * @cfg {Boolean} toggleSelect - selecting
15769 toggleSelect : false,
15772 * @cfg {Boolean} tickable - selecting
15777 * Returns the element this view is bound to.
15778 * @return {Roo.Element}
15780 getEl : function(){
15781 return this.wrapEl;
15787 * Refreshes the view. - called by datachanged on the store. - do not call directly.
15789 refresh : function(){
15790 //Roo.log('refresh');
15793 // if we are using something like 'domtemplate', then
15794 // the what gets used is:
15795 // t.applySubtemplate(NAME, data, wrapping data..)
15796 // the outer template then get' applied with
15797 // the store 'extra data'
15798 // and the body get's added to the
15799 // roo-name="data" node?
15800 // <span class='roo-tpl-{name}'></span> ?????
15804 this.clearSelections();
15805 this.el.update("");
15807 var records = this.store.getRange();
15808 if(records.length < 1) {
15810 // is this valid?? = should it render a template??
15812 this.el.update(this.emptyText);
15816 if (this.dataName) {
15817 this.el.update(t.apply(this.store.meta)); //????
15818 el = this.el.child('.roo-tpl-' + this.dataName);
15821 for(var i = 0, len = records.length; i < len; i++){
15822 var data = this.prepareData(records[i].data, i, records[i]);
15823 this.fireEvent("preparedata", this, data, i, records[i]);
15825 var d = Roo.apply({}, data);
15828 Roo.apply(d, {'roo-id' : Roo.id()});
15832 Roo.each(this.parent.item, function(item){
15833 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15836 Roo.apply(d, {'roo-data-checked' : 'checked'});
15840 html[html.length] = Roo.util.Format.trim(
15842 t.applySubtemplate(this.dataName, d, this.store.meta) :
15849 el.update(html.join(""));
15850 this.nodes = el.dom.childNodes;
15851 this.updateIndexes(0);
15856 * Function to override to reformat the data that is sent to
15857 * the template for each node.
15858 * DEPRICATED - use the preparedata event handler.
15859 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15860 * a JSON object for an UpdateManager bound view).
15862 prepareData : function(data, index, record)
15864 this.fireEvent("preparedata", this, data, index, record);
15868 onUpdate : function(ds, record){
15869 // Roo.log('on update');
15870 this.clearSelections();
15871 var index = this.store.indexOf(record);
15872 var n = this.nodes[index];
15873 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
15874 n.parentNode.removeChild(n);
15875 this.updateIndexes(index, index);
15881 onAdd : function(ds, records, index)
15883 //Roo.log(['on Add', ds, records, index] );
15884 this.clearSelections();
15885 if(this.nodes.length == 0){
15889 var n = this.nodes[index];
15890 for(var i = 0, len = records.length; i < len; i++){
15891 var d = this.prepareData(records[i].data, i, records[i]);
15893 this.tpl.insertBefore(n, d);
15896 this.tpl.append(this.el, d);
15899 this.updateIndexes(index);
15902 onRemove : function(ds, record, index){
15903 // Roo.log('onRemove');
15904 this.clearSelections();
15905 var el = this.dataName ?
15906 this.el.child('.roo-tpl-' + this.dataName) :
15909 el.dom.removeChild(this.nodes[index]);
15910 this.updateIndexes(index);
15914 * Refresh an individual node.
15915 * @param {Number} index
15917 refreshNode : function(index){
15918 this.onUpdate(this.store, this.store.getAt(index));
15921 updateIndexes : function(startIndex, endIndex){
15922 var ns = this.nodes;
15923 startIndex = startIndex || 0;
15924 endIndex = endIndex || ns.length - 1;
15925 for(var i = startIndex; i <= endIndex; i++){
15926 ns[i].nodeIndex = i;
15931 * Changes the data store this view uses and refresh the view.
15932 * @param {Store} store
15934 setStore : function(store, initial){
15935 if(!initial && this.store){
15936 this.store.un("datachanged", this.refresh);
15937 this.store.un("add", this.onAdd);
15938 this.store.un("remove", this.onRemove);
15939 this.store.un("update", this.onUpdate);
15940 this.store.un("clear", this.refresh);
15941 this.store.un("beforeload", this.onBeforeLoad);
15942 this.store.un("load", this.onLoad);
15943 this.store.un("loadexception", this.onLoad);
15947 store.on("datachanged", this.refresh, this);
15948 store.on("add", this.onAdd, this);
15949 store.on("remove", this.onRemove, this);
15950 store.on("update", this.onUpdate, this);
15951 store.on("clear", this.refresh, this);
15952 store.on("beforeload", this.onBeforeLoad, this);
15953 store.on("load", this.onLoad, this);
15954 store.on("loadexception", this.onLoad, this);
15962 * onbeforeLoad - masks the loading area.
15965 onBeforeLoad : function(store,opts)
15967 //Roo.log('onBeforeLoad');
15969 this.el.update("");
15971 this.el.mask(this.mask ? this.mask : "Loading" );
15973 onLoad : function ()
15980 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15981 * @param {HTMLElement} node
15982 * @return {HTMLElement} The template node
15984 findItemFromChild : function(node){
15985 var el = this.dataName ?
15986 this.el.child('.roo-tpl-' + this.dataName,true) :
15989 if(!node || node.parentNode == el){
15992 var p = node.parentNode;
15993 while(p && p != el){
15994 if(p.parentNode == el){
16003 onClick : function(e){
16004 var item = this.findItemFromChild(e.getTarget());
16006 var index = this.indexOf(item);
16007 if(this.onItemClick(item, index, e) !== false){
16008 this.fireEvent("click", this, index, item, e);
16011 this.clearSelections();
16016 onContextMenu : function(e){
16017 var item = this.findItemFromChild(e.getTarget());
16019 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16024 onDblClick : function(e){
16025 var item = this.findItemFromChild(e.getTarget());
16027 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16031 onItemClick : function(item, index, e)
16033 if(this.fireEvent("beforeclick", this, index, item, e) === false){
16036 if (this.toggleSelect) {
16037 var m = this.isSelected(item) ? 'unselect' : 'select';
16040 _t[m](item, true, false);
16043 if(this.multiSelect || this.singleSelect){
16044 if(this.multiSelect && e.shiftKey && this.lastSelection){
16045 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16047 this.select(item, this.multiSelect && e.ctrlKey);
16048 this.lastSelection = item;
16051 if(!this.tickable){
16052 e.preventDefault();
16060 * Get the number of selected nodes.
16063 getSelectionCount : function(){
16064 return this.selections.length;
16068 * Get the currently selected nodes.
16069 * @return {Array} An array of HTMLElements
16071 getSelectedNodes : function(){
16072 return this.selections;
16076 * Get the indexes of the selected nodes.
16079 getSelectedIndexes : function(){
16080 var indexes = [], s = this.selections;
16081 for(var i = 0, len = s.length; i < len; i++){
16082 indexes.push(s[i].nodeIndex);
16088 * Clear all selections
16089 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16091 clearSelections : function(suppressEvent){
16092 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16093 this.cmp.elements = this.selections;
16094 this.cmp.removeClass(this.selectedClass);
16095 this.selections = [];
16096 if(!suppressEvent){
16097 this.fireEvent("selectionchange", this, this.selections);
16103 * Returns true if the passed node is selected
16104 * @param {HTMLElement/Number} node The node or node index
16105 * @return {Boolean}
16107 isSelected : function(node){
16108 var s = this.selections;
16112 node = this.getNode(node);
16113 return s.indexOf(node) !== -1;
16118 * @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
16119 * @param {Boolean} keepExisting (optional) true to keep existing selections
16120 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16122 select : function(nodeInfo, keepExisting, suppressEvent){
16123 if(nodeInfo instanceof Array){
16125 this.clearSelections(true);
16127 for(var i = 0, len = nodeInfo.length; i < len; i++){
16128 this.select(nodeInfo[i], true, true);
16132 var node = this.getNode(nodeInfo);
16133 if(!node || this.isSelected(node)){
16134 return; // already selected.
16137 this.clearSelections(true);
16140 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16141 Roo.fly(node).addClass(this.selectedClass);
16142 this.selections.push(node);
16143 if(!suppressEvent){
16144 this.fireEvent("selectionchange", this, this.selections);
16152 * @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
16153 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16154 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16156 unselect : function(nodeInfo, keepExisting, suppressEvent)
16158 if(nodeInfo instanceof Array){
16159 Roo.each(this.selections, function(s) {
16160 this.unselect(s, nodeInfo);
16164 var node = this.getNode(nodeInfo);
16165 if(!node || !this.isSelected(node)){
16166 //Roo.log("not selected");
16167 return; // not selected.
16171 Roo.each(this.selections, function(s) {
16173 Roo.fly(node).removeClass(this.selectedClass);
16180 this.selections= ns;
16181 this.fireEvent("selectionchange", this, this.selections);
16185 * Gets a template node.
16186 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16187 * @return {HTMLElement} The node or null if it wasn't found
16189 getNode : function(nodeInfo){
16190 if(typeof nodeInfo == "string"){
16191 return document.getElementById(nodeInfo);
16192 }else if(typeof nodeInfo == "number"){
16193 return this.nodes[nodeInfo];
16199 * Gets a range template nodes.
16200 * @param {Number} startIndex
16201 * @param {Number} endIndex
16202 * @return {Array} An array of nodes
16204 getNodes : function(start, end){
16205 var ns = this.nodes;
16206 start = start || 0;
16207 end = typeof end == "undefined" ? ns.length - 1 : end;
16210 for(var i = start; i <= end; i++){
16214 for(var i = start; i >= end; i--){
16222 * Finds the index of the passed node
16223 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16224 * @return {Number} The index of the node or -1
16226 indexOf : function(node){
16227 node = this.getNode(node);
16228 if(typeof node.nodeIndex == "number"){
16229 return node.nodeIndex;
16231 var ns = this.nodes;
16232 for(var i = 0, len = ns.length; i < len; i++){
16243 * based on jquery fullcalendar
16247 Roo.bootstrap = Roo.bootstrap || {};
16249 * @class Roo.bootstrap.Calendar
16250 * @extends Roo.bootstrap.Component
16251 * Bootstrap Calendar class
16252 * @cfg {Boolean} loadMask (true|false) default false
16253 * @cfg {Object} header generate the user specific header of the calendar, default false
16256 * Create a new Container
16257 * @param {Object} config The config object
16262 Roo.bootstrap.Calendar = function(config){
16263 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16267 * Fires when a date is selected
16268 * @param {DatePicker} this
16269 * @param {Date} date The selected date
16273 * @event monthchange
16274 * Fires when the displayed month changes
16275 * @param {DatePicker} this
16276 * @param {Date} date The selected month
16278 'monthchange': true,
16280 * @event evententer
16281 * Fires when mouse over an event
16282 * @param {Calendar} this
16283 * @param {event} Event
16285 'evententer': true,
16287 * @event eventleave
16288 * Fires when the mouse leaves an
16289 * @param {Calendar} this
16292 'eventleave': true,
16294 * @event eventclick
16295 * Fires when the mouse click an
16296 * @param {Calendar} this
16305 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
16308 * @cfg {Number} startDay
16309 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16317 getAutoCreate : function(){
16320 var fc_button = function(name, corner, style, content ) {
16321 return Roo.apply({},{
16323 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
16325 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16328 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16339 style : 'width:100%',
16346 cls : 'fc-header-left',
16348 fc_button('prev', 'left', 'arrow', '‹' ),
16349 fc_button('next', 'right', 'arrow', '›' ),
16350 { tag: 'span', cls: 'fc-header-space' },
16351 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
16359 cls : 'fc-header-center',
16363 cls: 'fc-header-title',
16366 html : 'month / year'
16374 cls : 'fc-header-right',
16376 /* fc_button('month', 'left', '', 'month' ),
16377 fc_button('week', '', '', 'week' ),
16378 fc_button('day', 'right', '', 'day' )
16390 header = this.header;
16393 var cal_heads = function() {
16395 // fixme - handle this.
16397 for (var i =0; i < Date.dayNames.length; i++) {
16398 var d = Date.dayNames[i];
16401 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16402 html : d.substring(0,3)
16406 ret[0].cls += ' fc-first';
16407 ret[6].cls += ' fc-last';
16410 var cal_cell = function(n) {
16413 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16418 cls: 'fc-day-number',
16422 cls: 'fc-day-content',
16426 style: 'position: relative;' // height: 17px;
16438 var cal_rows = function() {
16441 for (var r = 0; r < 6; r++) {
16448 for (var i =0; i < Date.dayNames.length; i++) {
16449 var d = Date.dayNames[i];
16450 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16453 row.cn[0].cls+=' fc-first';
16454 row.cn[0].cn[0].style = 'min-height:90px';
16455 row.cn[6].cls+=' fc-last';
16459 ret[0].cls += ' fc-first';
16460 ret[4].cls += ' fc-prev-last';
16461 ret[5].cls += ' fc-last';
16468 cls: 'fc-border-separate',
16469 style : 'width:100%',
16477 cls : 'fc-first fc-last',
16495 cls : 'fc-content',
16496 style : "position: relative;",
16499 cls : 'fc-view fc-view-month fc-grid',
16500 style : 'position: relative',
16501 unselectable : 'on',
16504 cls : 'fc-event-container',
16505 style : 'position:absolute;z-index:8;top:0;left:0;'
16523 initEvents : function()
16526 throw "can not find store for calendar";
16532 style: "text-align:center",
16536 style: "background-color:white;width:50%;margin:250 auto",
16540 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
16551 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16553 var size = this.el.select('.fc-content', true).first().getSize();
16554 this.maskEl.setSize(size.width, size.height);
16555 this.maskEl.enableDisplayMode("block");
16556 if(!this.loadMask){
16557 this.maskEl.hide();
16560 this.store = Roo.factory(this.store, Roo.data);
16561 this.store.on('load', this.onLoad, this);
16562 this.store.on('beforeload', this.onBeforeLoad, this);
16566 this.cells = this.el.select('.fc-day',true);
16567 //Roo.log(this.cells);
16568 this.textNodes = this.el.query('.fc-day-number');
16569 this.cells.addClassOnOver('fc-state-hover');
16571 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16572 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16573 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16574 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16576 this.on('monthchange', this.onMonthChange, this);
16578 this.update(new Date().clearTime());
16581 resize : function() {
16582 var sz = this.el.getSize();
16584 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16585 this.el.select('.fc-day-content div',true).setHeight(34);
16590 showPrevMonth : function(e){
16591 this.update(this.activeDate.add("mo", -1));
16593 showToday : function(e){
16594 this.update(new Date().clearTime());
16597 showNextMonth : function(e){
16598 this.update(this.activeDate.add("mo", 1));
16602 showPrevYear : function(){
16603 this.update(this.activeDate.add("y", -1));
16607 showNextYear : function(){
16608 this.update(this.activeDate.add("y", 1));
16613 update : function(date)
16615 var vd = this.activeDate;
16616 this.activeDate = date;
16617 // if(vd && this.el){
16618 // var t = date.getTime();
16619 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16620 // Roo.log('using add remove');
16622 // this.fireEvent('monthchange', this, date);
16624 // this.cells.removeClass("fc-state-highlight");
16625 // this.cells.each(function(c){
16626 // if(c.dateValue == t){
16627 // c.addClass("fc-state-highlight");
16628 // setTimeout(function(){
16629 // try{c.dom.firstChild.focus();}catch(e){}
16639 var days = date.getDaysInMonth();
16641 var firstOfMonth = date.getFirstDateOfMonth();
16642 var startingPos = firstOfMonth.getDay()-this.startDay;
16644 if(startingPos < this.startDay){
16648 var pm = date.add(Date.MONTH, -1);
16649 var prevStart = pm.getDaysInMonth()-startingPos;
16651 this.cells = this.el.select('.fc-day',true);
16652 this.textNodes = this.el.query('.fc-day-number');
16653 this.cells.addClassOnOver('fc-state-hover');
16655 var cells = this.cells.elements;
16656 var textEls = this.textNodes;
16658 Roo.each(cells, function(cell){
16659 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16662 days += startingPos;
16664 // convert everything to numbers so it's fast
16665 var day = 86400000;
16666 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16669 //Roo.log(prevStart);
16671 var today = new Date().clearTime().getTime();
16672 var sel = date.clearTime().getTime();
16673 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16674 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16675 var ddMatch = this.disabledDatesRE;
16676 var ddText = this.disabledDatesText;
16677 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16678 var ddaysText = this.disabledDaysText;
16679 var format = this.format;
16681 var setCellClass = function(cal, cell){
16685 //Roo.log('set Cell Class');
16687 var t = d.getTime();
16691 cell.dateValue = t;
16693 cell.className += " fc-today";
16694 cell.className += " fc-state-highlight";
16695 cell.title = cal.todayText;
16698 // disable highlight in other month..
16699 //cell.className += " fc-state-highlight";
16704 cell.className = " fc-state-disabled";
16705 cell.title = cal.minText;
16709 cell.className = " fc-state-disabled";
16710 cell.title = cal.maxText;
16714 if(ddays.indexOf(d.getDay()) != -1){
16715 cell.title = ddaysText;
16716 cell.className = " fc-state-disabled";
16719 if(ddMatch && format){
16720 var fvalue = d.dateFormat(format);
16721 if(ddMatch.test(fvalue)){
16722 cell.title = ddText.replace("%0", fvalue);
16723 cell.className = " fc-state-disabled";
16727 if (!cell.initialClassName) {
16728 cell.initialClassName = cell.dom.className;
16731 cell.dom.className = cell.initialClassName + ' ' + cell.className;
16736 for(; i < startingPos; i++) {
16737 textEls[i].innerHTML = (++prevStart);
16738 d.setDate(d.getDate()+1);
16740 cells[i].className = "fc-past fc-other-month";
16741 setCellClass(this, cells[i]);
16746 for(; i < days; i++){
16747 intDay = i - startingPos + 1;
16748 textEls[i].innerHTML = (intDay);
16749 d.setDate(d.getDate()+1);
16751 cells[i].className = ''; // "x-date-active";
16752 setCellClass(this, cells[i]);
16756 for(; i < 42; i++) {
16757 textEls[i].innerHTML = (++extraDays);
16758 d.setDate(d.getDate()+1);
16760 cells[i].className = "fc-future fc-other-month";
16761 setCellClass(this, cells[i]);
16764 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16766 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16768 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16769 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16771 if(totalRows != 6){
16772 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16773 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16776 this.fireEvent('monthchange', this, date);
16780 if(!this.internalRender){
16781 var main = this.el.dom.firstChild;
16782 var w = main.offsetWidth;
16783 this.el.setWidth(w + this.el.getBorderWidth("lr"));
16784 Roo.fly(main).setWidth(w);
16785 this.internalRender = true;
16786 // opera does not respect the auto grow header center column
16787 // then, after it gets a width opera refuses to recalculate
16788 // without a second pass
16789 if(Roo.isOpera && !this.secondPass){
16790 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16791 this.secondPass = true;
16792 this.update.defer(10, this, [date]);
16799 findCell : function(dt) {
16800 dt = dt.clearTime().getTime();
16802 this.cells.each(function(c){
16803 //Roo.log("check " +c.dateValue + '?=' + dt);
16804 if(c.dateValue == dt){
16814 findCells : function(ev) {
16815 var s = ev.start.clone().clearTime().getTime();
16817 var e= ev.end.clone().clearTime().getTime();
16820 this.cells.each(function(c){
16821 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16823 if(c.dateValue > e){
16826 if(c.dateValue < s){
16835 // findBestRow: function(cells)
16839 // for (var i =0 ; i < cells.length;i++) {
16840 // ret = Math.max(cells[i].rows || 0,ret);
16847 addItem : function(ev)
16849 // look for vertical location slot in
16850 var cells = this.findCells(ev);
16852 // ev.row = this.findBestRow(cells);
16854 // work out the location.
16858 for(var i =0; i < cells.length; i++) {
16860 cells[i].row = cells[0].row;
16863 cells[i].row = cells[i].row + 1;
16873 if (crow.start.getY() == cells[i].getY()) {
16875 crow.end = cells[i];
16892 cells[0].events.push(ev);
16894 this.calevents.push(ev);
16897 clearEvents: function() {
16899 if(!this.calevents){
16903 Roo.each(this.cells.elements, function(c){
16909 Roo.each(this.calevents, function(e) {
16910 Roo.each(e.els, function(el) {
16911 el.un('mouseenter' ,this.onEventEnter, this);
16912 el.un('mouseleave' ,this.onEventLeave, this);
16917 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16923 renderEvents: function()
16927 this.cells.each(function(c) {
16936 if(c.row != c.events.length){
16937 r = 4 - (4 - (c.row - c.events.length));
16940 c.events = ev.slice(0, r);
16941 c.more = ev.slice(r);
16943 if(c.more.length && c.more.length == 1){
16944 c.events.push(c.more.pop());
16947 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16951 this.cells.each(function(c) {
16953 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16956 for (var e = 0; e < c.events.length; e++){
16957 var ev = c.events[e];
16958 var rows = ev.rows;
16960 for(var i = 0; i < rows.length; i++) {
16962 // how many rows should it span..
16965 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16966 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16968 unselectable : "on",
16971 cls: 'fc-event-inner',
16975 // cls: 'fc-event-time',
16976 // html : cells.length > 1 ? '' : ev.time
16980 cls: 'fc-event-title',
16981 html : String.format('{0}', ev.title)
16988 cls: 'ui-resizable-handle ui-resizable-e',
16989 html : '  '
16996 cfg.cls += ' fc-event-start';
16998 if ((i+1) == rows.length) {
16999 cfg.cls += ' fc-event-end';
17002 var ctr = _this.el.select('.fc-event-container',true).first();
17003 var cg = ctr.createChild(cfg);
17005 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17006 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17008 var r = (c.more.length) ? 1 : 0;
17009 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
17010 cg.setWidth(ebox.right - sbox.x -2);
17012 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17013 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17014 cg.on('click', _this.onEventClick, _this, ev);
17025 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17026 style : 'position: absolute',
17027 unselectable : "on",
17030 cls: 'fc-event-inner',
17034 cls: 'fc-event-title',
17042 cls: 'ui-resizable-handle ui-resizable-e',
17043 html : '  '
17049 var ctr = _this.el.select('.fc-event-container',true).first();
17050 var cg = ctr.createChild(cfg);
17052 var sbox = c.select('.fc-day-content',true).first().getBox();
17053 var ebox = c.select('.fc-day-content',true).first().getBox();
17055 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
17056 cg.setWidth(ebox.right - sbox.x -2);
17058 cg.on('click', _this.onMoreEventClick, _this, c.more);
17068 onEventEnter: function (e, el,event,d) {
17069 this.fireEvent('evententer', this, el, event);
17072 onEventLeave: function (e, el,event,d) {
17073 this.fireEvent('eventleave', this, el, event);
17076 onEventClick: function (e, el,event,d) {
17077 this.fireEvent('eventclick', this, el, event);
17080 onMonthChange: function () {
17084 onMoreEventClick: function(e, el, more)
17088 this.calpopover.placement = 'right';
17089 this.calpopover.setTitle('More');
17091 this.calpopover.setContent('');
17093 var ctr = this.calpopover.el.select('.popover-content', true).first();
17095 Roo.each(more, function(m){
17097 cls : 'fc-event-hori fc-event-draggable',
17100 var cg = ctr.createChild(cfg);
17102 cg.on('click', _this.onEventClick, _this, m);
17105 this.calpopover.show(el);
17110 onLoad: function ()
17112 this.calevents = [];
17115 if(this.store.getCount() > 0){
17116 this.store.data.each(function(d){
17119 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17120 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17121 time : d.data.start_time,
17122 title : d.data.title,
17123 description : d.data.description,
17124 venue : d.data.venue
17129 this.renderEvents();
17131 if(this.calevents.length && this.loadMask){
17132 this.maskEl.hide();
17136 onBeforeLoad: function()
17138 this.clearEvents();
17140 this.maskEl.show();
17154 * @class Roo.bootstrap.Popover
17155 * @extends Roo.bootstrap.Component
17156 * Bootstrap Popover class
17157 * @cfg {String} html contents of the popover (or false to use children..)
17158 * @cfg {String} title of popover (or false to hide)
17159 * @cfg {String} placement how it is placed
17160 * @cfg {String} trigger click || hover (or false to trigger manually)
17161 * @cfg {String} over what (parent or false to trigger manually.)
17162 * @cfg {Number} delay - delay before showing
17165 * Create a new Popover
17166 * @param {Object} config The config object
17169 Roo.bootstrap.Popover = function(config){
17170 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17176 * After the popover show
17178 * @param {Roo.bootstrap.Popover} this
17183 * After the popover hide
17185 * @param {Roo.bootstrap.Popover} this
17191 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
17193 title: 'Fill in a title',
17196 placement : 'right',
17197 trigger : 'hover', // hover
17203 can_build_overlaid : false,
17205 getChildContainer : function()
17207 return this.el.select('.popover-content',true).first();
17210 getAutoCreate : function(){
17213 cls : 'popover roo-dynamic',
17214 style: 'display:block',
17220 cls : 'popover-inner',
17224 cls: 'popover-title',
17228 cls : 'popover-content',
17239 setTitle: function(str)
17242 this.el.select('.popover-title',true).first().dom.innerHTML = str;
17244 setContent: function(str)
17247 this.el.select('.popover-content',true).first().dom.innerHTML = str;
17249 // as it get's added to the bottom of the page.
17250 onRender : function(ct, position)
17252 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17254 var cfg = Roo.apply({}, this.getAutoCreate());
17258 cfg.cls += ' ' + this.cls;
17261 cfg.style = this.style;
17263 //Roo.log("adding to ");
17264 this.el = Roo.get(document.body).createChild(cfg, position);
17265 // Roo.log(this.el);
17270 initEvents : function()
17272 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17273 this.el.enableDisplayMode('block');
17275 if (this.over === false) {
17278 if (this.triggers === false) {
17281 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17282 var triggers = this.trigger ? this.trigger.split(' ') : [];
17283 Roo.each(triggers, function(trigger) {
17285 if (trigger == 'click') {
17286 on_el.on('click', this.toggle, this);
17287 } else if (trigger != 'manual') {
17288 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
17289 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17291 on_el.on(eventIn ,this.enter, this);
17292 on_el.on(eventOut, this.leave, this);
17303 toggle : function () {
17304 this.hoverState == 'in' ? this.leave() : this.enter();
17307 enter : function () {
17309 clearTimeout(this.timeout);
17311 this.hoverState = 'in';
17313 if (!this.delay || !this.delay.show) {
17318 this.timeout = setTimeout(function () {
17319 if (_t.hoverState == 'in') {
17322 }, this.delay.show)
17325 leave : function() {
17326 clearTimeout(this.timeout);
17328 this.hoverState = 'out';
17330 if (!this.delay || !this.delay.hide) {
17335 this.timeout = setTimeout(function () {
17336 if (_t.hoverState == 'out') {
17339 }, this.delay.hide)
17342 show : function (on_el)
17345 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17349 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17350 if (this.html !== false) {
17351 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17353 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17354 if (!this.title.length) {
17355 this.el.select('.popover-title',true).hide();
17358 var placement = typeof this.placement == 'function' ?
17359 this.placement.call(this, this.el, on_el) :
17362 var autoToken = /\s?auto?\s?/i;
17363 var autoPlace = autoToken.test(placement);
17365 placement = placement.replace(autoToken, '') || 'top';
17369 //this.el.setXY([0,0]);
17371 this.el.dom.style.display='block';
17372 this.el.addClass(placement);
17374 //this.el.appendTo(on_el);
17376 var p = this.getPosition();
17377 var box = this.el.getBox();
17382 var align = Roo.bootstrap.Popover.alignment[placement];
17385 this.el.alignTo(on_el, align[0],align[1]);
17386 //var arrow = this.el.select('.arrow',true).first();
17387 //arrow.set(align[2],
17389 this.el.addClass('in');
17392 if (this.el.hasClass('fade')) {
17396 this.hoverState = 'in';
17398 this.fireEvent('show', this);
17403 this.el.setXY([0,0]);
17404 this.el.removeClass('in');
17406 this.hoverState = null;
17408 this.fireEvent('hide', this);
17413 Roo.bootstrap.Popover.alignment = {
17414 'left' : ['r-l', [-10,0], 'right'],
17415 'right' : ['l-r', [10,0], 'left'],
17416 'bottom' : ['t-b', [0,10], 'top'],
17417 'top' : [ 'b-t', [0,-10], 'bottom']
17428 * @class Roo.bootstrap.Progress
17429 * @extends Roo.bootstrap.Component
17430 * Bootstrap Progress class
17431 * @cfg {Boolean} striped striped of the progress bar
17432 * @cfg {Boolean} active animated of the progress bar
17436 * Create a new Progress
17437 * @param {Object} config The config object
17440 Roo.bootstrap.Progress = function(config){
17441 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17444 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
17449 getAutoCreate : function(){
17457 cfg.cls += ' progress-striped';
17461 cfg.cls += ' active';
17480 * @class Roo.bootstrap.ProgressBar
17481 * @extends Roo.bootstrap.Component
17482 * Bootstrap ProgressBar class
17483 * @cfg {Number} aria_valuenow aria-value now
17484 * @cfg {Number} aria_valuemin aria-value min
17485 * @cfg {Number} aria_valuemax aria-value max
17486 * @cfg {String} label label for the progress bar
17487 * @cfg {String} panel (success | info | warning | danger )
17488 * @cfg {String} role role of the progress bar
17489 * @cfg {String} sr_only text
17493 * Create a new ProgressBar
17494 * @param {Object} config The config object
17497 Roo.bootstrap.ProgressBar = function(config){
17498 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17501 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
17505 aria_valuemax : 100,
17511 getAutoCreate : function()
17516 cls: 'progress-bar',
17517 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17529 cfg.role = this.role;
17532 if(this.aria_valuenow){
17533 cfg['aria-valuenow'] = this.aria_valuenow;
17536 if(this.aria_valuemin){
17537 cfg['aria-valuemin'] = this.aria_valuemin;
17540 if(this.aria_valuemax){
17541 cfg['aria-valuemax'] = this.aria_valuemax;
17544 if(this.label && !this.sr_only){
17545 cfg.html = this.label;
17549 cfg.cls += ' progress-bar-' + this.panel;
17555 update : function(aria_valuenow)
17557 this.aria_valuenow = aria_valuenow;
17559 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17574 * @class Roo.bootstrap.TabGroup
17575 * @extends Roo.bootstrap.Column
17576 * Bootstrap Column class
17577 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17578 * @cfg {Boolean} carousel true to make the group behave like a carousel
17579 * @cfg {Boolean} bullets show bullets for the panels
17580 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17581 * @cfg {Number} timer auto slide timer .. default 0 millisecond
17582 * @cfg {Boolean} showarrow (true|false) show arrow default true
17585 * Create a new TabGroup
17586 * @param {Object} config The config object
17589 Roo.bootstrap.TabGroup = function(config){
17590 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17592 this.navId = Roo.id();
17595 Roo.bootstrap.TabGroup.register(this);
17599 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
17602 transition : false,
17607 slideOnTouch : false,
17610 getAutoCreate : function()
17612 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17614 cfg.cls += ' tab-content';
17616 if (this.carousel) {
17617 cfg.cls += ' carousel slide';
17620 cls : 'carousel-inner',
17624 if(this.bullets && !Roo.isTouch){
17627 cls : 'carousel-bullets',
17631 if(this.bullets_cls){
17632 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17639 cfg.cn[0].cn.push(bullets);
17642 if(this.showarrow){
17643 cfg.cn[0].cn.push({
17645 class : 'carousel-arrow',
17649 class : 'carousel-prev',
17653 class : 'fa fa-chevron-left'
17659 class : 'carousel-next',
17663 class : 'fa fa-chevron-right'
17676 initEvents: function()
17678 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17679 // this.el.on("touchstart", this.onTouchStart, this);
17682 if(this.autoslide){
17685 this.slideFn = window.setInterval(function() {
17686 _this.showPanelNext();
17690 if(this.showarrow){
17691 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17692 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17698 // onTouchStart : function(e, el, o)
17700 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17704 // this.showPanelNext();
17708 getChildContainer : function()
17710 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17714 * register a Navigation item
17715 * @param {Roo.bootstrap.NavItem} the navitem to add
17717 register : function(item)
17719 this.tabs.push( item);
17720 item.navId = this.navId; // not really needed..
17725 getActivePanel : function()
17728 Roo.each(this.tabs, function(t) {
17738 getPanelByName : function(n)
17741 Roo.each(this.tabs, function(t) {
17742 if (t.tabId == n) {
17750 indexOfPanel : function(p)
17753 Roo.each(this.tabs, function(t,i) {
17754 if (t.tabId == p.tabId) {
17763 * show a specific panel
17764 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17765 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17767 showPanel : function (pan)
17769 if(this.transition || typeof(pan) == 'undefined'){
17770 Roo.log("waiting for the transitionend");
17774 if (typeof(pan) == 'number') {
17775 pan = this.tabs[pan];
17778 if (typeof(pan) == 'string') {
17779 pan = this.getPanelByName(pan);
17782 var cur = this.getActivePanel();
17785 Roo.log('pan or acitve pan is undefined');
17789 if (pan.tabId == this.getActivePanel().tabId) {
17793 if (false === cur.fireEvent('beforedeactivate')) {
17797 if(this.bullets > 0 && !Roo.isTouch){
17798 this.setActiveBullet(this.indexOfPanel(pan));
17801 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17803 this.transition = true;
17804 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
17805 var lr = dir == 'next' ? 'left' : 'right';
17806 pan.el.addClass(dir); // or prev
17807 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17808 cur.el.addClass(lr); // or right
17809 pan.el.addClass(lr);
17812 cur.el.on('transitionend', function() {
17813 Roo.log("trans end?");
17815 pan.el.removeClass([lr,dir]);
17816 pan.setActive(true);
17818 cur.el.removeClass([lr]);
17819 cur.setActive(false);
17821 _this.transition = false;
17823 }, this, { single: true } );
17828 cur.setActive(false);
17829 pan.setActive(true);
17834 showPanelNext : function()
17836 var i = this.indexOfPanel(this.getActivePanel());
17838 if (i >= this.tabs.length - 1 && !this.autoslide) {
17842 if (i >= this.tabs.length - 1 && this.autoslide) {
17846 this.showPanel(this.tabs[i+1]);
17849 showPanelPrev : function()
17851 var i = this.indexOfPanel(this.getActivePanel());
17853 if (i < 1 && !this.autoslide) {
17857 if (i < 1 && this.autoslide) {
17858 i = this.tabs.length;
17861 this.showPanel(this.tabs[i-1]);
17865 addBullet: function()
17867 if(!this.bullets || Roo.isTouch){
17870 var ctr = this.el.select('.carousel-bullets',true).first();
17871 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
17872 var bullet = ctr.createChild({
17873 cls : 'bullet bullet-' + i
17874 },ctr.dom.lastChild);
17879 bullet.on('click', (function(e, el, o, ii, t){
17881 e.preventDefault();
17883 this.showPanel(ii);
17885 if(this.autoslide && this.slideFn){
17886 clearInterval(this.slideFn);
17887 this.slideFn = window.setInterval(function() {
17888 _this.showPanelNext();
17892 }).createDelegate(this, [i, bullet], true));
17897 setActiveBullet : function(i)
17903 Roo.each(this.el.select('.bullet', true).elements, function(el){
17904 el.removeClass('selected');
17907 var bullet = this.el.select('.bullet-' + i, true).first();
17913 bullet.addClass('selected');
17924 Roo.apply(Roo.bootstrap.TabGroup, {
17928 * register a Navigation Group
17929 * @param {Roo.bootstrap.NavGroup} the navgroup to add
17931 register : function(navgrp)
17933 this.groups[navgrp.navId] = navgrp;
17937 * fetch a Navigation Group based on the navigation ID
17938 * if one does not exist , it will get created.
17939 * @param {string} the navgroup to add
17940 * @returns {Roo.bootstrap.NavGroup} the navgroup
17942 get: function(navId) {
17943 if (typeof(this.groups[navId]) == 'undefined') {
17944 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17946 return this.groups[navId] ;
17961 * @class Roo.bootstrap.TabPanel
17962 * @extends Roo.bootstrap.Component
17963 * Bootstrap TabPanel class
17964 * @cfg {Boolean} active panel active
17965 * @cfg {String} html panel content
17966 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17967 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17968 * @cfg {String} href click to link..
17972 * Create a new TabPanel
17973 * @param {Object} config The config object
17976 Roo.bootstrap.TabPanel = function(config){
17977 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17981 * Fires when the active status changes
17982 * @param {Roo.bootstrap.TabPanel} this
17983 * @param {Boolean} state the new state
17988 * @event beforedeactivate
17989 * Fires before a tab is de-activated - can be used to do validation on a form.
17990 * @param {Roo.bootstrap.TabPanel} this
17991 * @return {Boolean} false if there is an error
17994 'beforedeactivate': true
17997 this.tabId = this.tabId || Roo.id();
18001 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
18009 getAutoCreate : function(){
18012 // item is needed for carousel - not sure if it has any effect otherwise
18013 cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18014 html: this.html || ''
18018 cfg.cls += ' active';
18022 cfg.tabId = this.tabId;
18029 initEvents: function()
18031 var p = this.parent();
18033 this.navId = this.navId || p.navId;
18035 if (typeof(this.navId) != 'undefined') {
18036 // not really needed.. but just in case.. parent should be a NavGroup.
18037 var tg = Roo.bootstrap.TabGroup.get(this.navId);
18041 var i = tg.tabs.length - 1;
18043 if(this.active && tg.bullets > 0 && i < tg.bullets){
18044 tg.setActiveBullet(i);
18048 this.el.on('click', this.onClick, this);
18051 this.el.on("touchstart", this.onTouchStart, this);
18052 this.el.on("touchmove", this.onTouchMove, this);
18053 this.el.on("touchend", this.onTouchEnd, this);
18058 onRender : function(ct, position)
18060 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18063 setActive : function(state)
18065 Roo.log("panel - set active " + this.tabId + "=" + state);
18067 this.active = state;
18069 this.el.removeClass('active');
18071 } else if (!this.el.hasClass('active')) {
18072 this.el.addClass('active');
18075 this.fireEvent('changed', this, state);
18078 onClick : function(e)
18080 e.preventDefault();
18082 if(!this.href.length){
18086 window.location.href = this.href;
18095 onTouchStart : function(e)
18097 this.swiping = false;
18099 this.startX = e.browserEvent.touches[0].clientX;
18100 this.startY = e.browserEvent.touches[0].clientY;
18103 onTouchMove : function(e)
18105 this.swiping = true;
18107 this.endX = e.browserEvent.touches[0].clientX;
18108 this.endY = e.browserEvent.touches[0].clientY;
18111 onTouchEnd : function(e)
18118 var tabGroup = this.parent();
18120 if(this.endX > this.startX){ // swiping right
18121 tabGroup.showPanelPrev();
18125 if(this.startX > this.endX){ // swiping left
18126 tabGroup.showPanelNext();
18145 * @class Roo.bootstrap.DateField
18146 * @extends Roo.bootstrap.Input
18147 * Bootstrap DateField class
18148 * @cfg {Number} weekStart default 0
18149 * @cfg {String} viewMode default empty, (months|years)
18150 * @cfg {String} minViewMode default empty, (months|years)
18151 * @cfg {Number} startDate default -Infinity
18152 * @cfg {Number} endDate default Infinity
18153 * @cfg {Boolean} todayHighlight default false
18154 * @cfg {Boolean} todayBtn default false
18155 * @cfg {Boolean} calendarWeeks default false
18156 * @cfg {Object} daysOfWeekDisabled default empty
18157 * @cfg {Boolean} singleMode default false (true | false)
18159 * @cfg {Boolean} keyboardNavigation default true
18160 * @cfg {String} language default en
18163 * Create a new DateField
18164 * @param {Object} config The config object
18167 Roo.bootstrap.DateField = function(config){
18168 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18172 * Fires when this field show.
18173 * @param {Roo.bootstrap.DateField} this
18174 * @param {Mixed} date The date value
18179 * Fires when this field hide.
18180 * @param {Roo.bootstrap.DateField} this
18181 * @param {Mixed} date The date value
18186 * Fires when select a date.
18187 * @param {Roo.bootstrap.DateField} this
18188 * @param {Mixed} date The date value
18192 * @event beforeselect
18193 * Fires when before select a date.
18194 * @param {Roo.bootstrap.DateField} this
18195 * @param {Mixed} date The date value
18197 beforeselect : true
18201 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
18204 * @cfg {String} format
18205 * The default date format string which can be overriden for localization support. The format must be
18206 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18210 * @cfg {String} altFormats
18211 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18212 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18214 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18222 todayHighlight : false,
18228 keyboardNavigation: true,
18230 calendarWeeks: false,
18232 startDate: -Infinity,
18236 daysOfWeekDisabled: [],
18240 singleMode : false,
18242 UTCDate: function()
18244 return new Date(Date.UTC.apply(Date, arguments));
18247 UTCToday: function()
18249 var today = new Date();
18250 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18253 getDate: function() {
18254 var d = this.getUTCDate();
18255 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18258 getUTCDate: function() {
18262 setDate: function(d) {
18263 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18266 setUTCDate: function(d) {
18268 this.setValue(this.formatDate(this.date));
18271 onRender: function(ct, position)
18274 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18276 this.language = this.language || 'en';
18277 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18278 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18280 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18281 this.format = this.format || 'm/d/y';
18282 this.isInline = false;
18283 this.isInput = true;
18284 this.component = this.el.select('.add-on', true).first() || false;
18285 this.component = (this.component && this.component.length === 0) ? false : this.component;
18286 this.hasInput = this.component && this.inputEl().length;
18288 if (typeof(this.minViewMode === 'string')) {
18289 switch (this.minViewMode) {
18291 this.minViewMode = 1;
18294 this.minViewMode = 2;
18297 this.minViewMode = 0;
18302 if (typeof(this.viewMode === 'string')) {
18303 switch (this.viewMode) {
18316 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18318 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18320 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18322 this.picker().on('mousedown', this.onMousedown, this);
18323 this.picker().on('click', this.onClick, this);
18325 this.picker().addClass('datepicker-dropdown');
18327 this.startViewMode = this.viewMode;
18329 if(this.singleMode){
18330 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18331 v.setVisibilityMode(Roo.Element.DISPLAY);
18335 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18336 v.setStyle('width', '189px');
18340 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18341 if(!this.calendarWeeks){
18346 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18347 v.attr('colspan', function(i, val){
18348 return parseInt(val) + 1;
18353 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18355 this.setStartDate(this.startDate);
18356 this.setEndDate(this.endDate);
18358 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18365 if(this.isInline) {
18370 picker : function()
18372 return this.pickerEl;
18373 // return this.el.select('.datepicker', true).first();
18376 fillDow: function()
18378 var dowCnt = this.weekStart;
18387 if(this.calendarWeeks){
18395 while (dowCnt < this.weekStart + 7) {
18399 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18403 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18406 fillMonths: function()
18409 var months = this.picker().select('>.datepicker-months td', true).first();
18411 months.dom.innerHTML = '';
18417 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18420 months.createChild(month);
18427 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;
18429 if (this.date < this.startDate) {
18430 this.viewDate = new Date(this.startDate);
18431 } else if (this.date > this.endDate) {
18432 this.viewDate = new Date(this.endDate);
18434 this.viewDate = new Date(this.date);
18442 var d = new Date(this.viewDate),
18443 year = d.getUTCFullYear(),
18444 month = d.getUTCMonth(),
18445 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18446 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18447 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18448 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18449 currentDate = this.date && this.date.valueOf(),
18450 today = this.UTCToday();
18452 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18454 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18456 // this.picker.select('>tfoot th.today').
18457 // .text(dates[this.language].today)
18458 // .toggle(this.todayBtn !== false);
18460 this.updateNavArrows();
18463 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18465 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18467 prevMonth.setUTCDate(day);
18469 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18471 var nextMonth = new Date(prevMonth);
18473 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18475 nextMonth = nextMonth.valueOf();
18477 var fillMonths = false;
18479 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18481 while(prevMonth.valueOf() < nextMonth) {
18484 if (prevMonth.getUTCDay() === this.weekStart) {
18486 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18494 if(this.calendarWeeks){
18495 // ISO 8601: First week contains first thursday.
18496 // ISO also states week starts on Monday, but we can be more abstract here.
18498 // Start of current week: based on weekstart/current date
18499 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18500 // Thursday of this week
18501 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18502 // First Thursday of year, year from thursday
18503 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18504 // Calendar week: ms between thursdays, div ms per day, div 7 days
18505 calWeek = (th - yth) / 864e5 / 7 + 1;
18507 fillMonths.cn.push({
18515 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18517 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18520 if (this.todayHighlight &&
18521 prevMonth.getUTCFullYear() == today.getFullYear() &&
18522 prevMonth.getUTCMonth() == today.getMonth() &&
18523 prevMonth.getUTCDate() == today.getDate()) {
18524 clsName += ' today';
18527 if (currentDate && prevMonth.valueOf() === currentDate) {
18528 clsName += ' active';
18531 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18532 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18533 clsName += ' disabled';
18536 fillMonths.cn.push({
18538 cls: 'day ' + clsName,
18539 html: prevMonth.getDate()
18542 prevMonth.setDate(prevMonth.getDate()+1);
18545 var currentYear = this.date && this.date.getUTCFullYear();
18546 var currentMonth = this.date && this.date.getUTCMonth();
18548 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18550 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18551 v.removeClass('active');
18553 if(currentYear === year && k === currentMonth){
18554 v.addClass('active');
18557 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18558 v.addClass('disabled');
18564 year = parseInt(year/10, 10) * 10;
18566 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18568 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18571 for (var i = -1; i < 11; i++) {
18572 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18574 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18582 showMode: function(dir)
18585 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18588 Roo.each(this.picker().select('>div',true).elements, function(v){
18589 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18592 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18597 if(this.isInline) {
18601 this.picker().removeClass(['bottom', 'top']);
18603 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18605 * place to the top of element!
18609 this.picker().addClass('top');
18610 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18615 this.picker().addClass('bottom');
18617 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18620 parseDate : function(value)
18622 if(!value || value instanceof Date){
18625 var v = Date.parseDate(value, this.format);
18626 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18627 v = Date.parseDate(value, 'Y-m-d');
18629 if(!v && this.altFormats){
18630 if(!this.altFormatsArray){
18631 this.altFormatsArray = this.altFormats.split("|");
18633 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18634 v = Date.parseDate(value, this.altFormatsArray[i]);
18640 formatDate : function(date, fmt)
18642 return (!date || !(date instanceof Date)) ?
18643 date : date.dateFormat(fmt || this.format);
18646 onFocus : function()
18648 Roo.bootstrap.DateField.superclass.onFocus.call(this);
18652 onBlur : function()
18654 Roo.bootstrap.DateField.superclass.onBlur.call(this);
18656 var d = this.inputEl().getValue();
18665 this.picker().show();
18669 this.fireEvent('show', this, this.date);
18674 if(this.isInline) {
18677 this.picker().hide();
18678 this.viewMode = this.startViewMode;
18681 this.fireEvent('hide', this, this.date);
18685 onMousedown: function(e)
18687 e.stopPropagation();
18688 e.preventDefault();
18693 Roo.bootstrap.DateField.superclass.keyup.call(this);
18697 setValue: function(v)
18699 if(this.fireEvent('beforeselect', this, v) !== false){
18700 var d = new Date(this.parseDate(v) ).clearTime();
18702 if(isNaN(d.getTime())){
18703 this.date = this.viewDate = '';
18704 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18708 v = this.formatDate(d);
18710 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18712 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18716 this.fireEvent('select', this, this.date);
18720 getValue: function()
18722 return this.formatDate(this.date);
18725 fireKey: function(e)
18727 if (!this.picker().isVisible()){
18728 if (e.keyCode == 27) { // allow escape to hide and re-show picker
18734 var dateChanged = false,
18736 newDate, newViewDate;
18741 e.preventDefault();
18745 if (!this.keyboardNavigation) {
18748 dir = e.keyCode == 37 ? -1 : 1;
18751 newDate = this.moveYear(this.date, dir);
18752 newViewDate = this.moveYear(this.viewDate, dir);
18753 } else if (e.shiftKey){
18754 newDate = this.moveMonth(this.date, dir);
18755 newViewDate = this.moveMonth(this.viewDate, dir);
18757 newDate = new Date(this.date);
18758 newDate.setUTCDate(this.date.getUTCDate() + dir);
18759 newViewDate = new Date(this.viewDate);
18760 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18762 if (this.dateWithinRange(newDate)){
18763 this.date = newDate;
18764 this.viewDate = newViewDate;
18765 this.setValue(this.formatDate(this.date));
18767 e.preventDefault();
18768 dateChanged = true;
18773 if (!this.keyboardNavigation) {
18776 dir = e.keyCode == 38 ? -1 : 1;
18778 newDate = this.moveYear(this.date, dir);
18779 newViewDate = this.moveYear(this.viewDate, dir);
18780 } else if (e.shiftKey){
18781 newDate = this.moveMonth(this.date, dir);
18782 newViewDate = this.moveMonth(this.viewDate, dir);
18784 newDate = new Date(this.date);
18785 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18786 newViewDate = new Date(this.viewDate);
18787 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18789 if (this.dateWithinRange(newDate)){
18790 this.date = newDate;
18791 this.viewDate = newViewDate;
18792 this.setValue(this.formatDate(this.date));
18794 e.preventDefault();
18795 dateChanged = true;
18799 this.setValue(this.formatDate(this.date));
18801 e.preventDefault();
18804 this.setValue(this.formatDate(this.date));
18818 onClick: function(e)
18820 e.stopPropagation();
18821 e.preventDefault();
18823 var target = e.getTarget();
18825 if(target.nodeName.toLowerCase() === 'i'){
18826 target = Roo.get(target).dom.parentNode;
18829 var nodeName = target.nodeName;
18830 var className = target.className;
18831 var html = target.innerHTML;
18832 //Roo.log(nodeName);
18834 switch(nodeName.toLowerCase()) {
18836 switch(className) {
18842 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18843 switch(this.viewMode){
18845 this.viewDate = this.moveMonth(this.viewDate, dir);
18849 this.viewDate = this.moveYear(this.viewDate, dir);
18855 var date = new Date();
18856 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18858 this.setValue(this.formatDate(this.date));
18865 if (className.indexOf('disabled') < 0) {
18866 this.viewDate.setUTCDate(1);
18867 if (className.indexOf('month') > -1) {
18868 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18870 var year = parseInt(html, 10) || 0;
18871 this.viewDate.setUTCFullYear(year);
18875 if(this.singleMode){
18876 this.setValue(this.formatDate(this.viewDate));
18887 //Roo.log(className);
18888 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
18889 var day = parseInt(html, 10) || 1;
18890 var year = this.viewDate.getUTCFullYear(),
18891 month = this.viewDate.getUTCMonth();
18893 if (className.indexOf('old') > -1) {
18900 } else if (className.indexOf('new') > -1) {
18908 //Roo.log([year,month,day]);
18909 this.date = this.UTCDate(year, month, day,0,0,0,0);
18910 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
18912 //Roo.log(this.formatDate(this.date));
18913 this.setValue(this.formatDate(this.date));
18920 setStartDate: function(startDate)
18922 this.startDate = startDate || -Infinity;
18923 if (this.startDate !== -Infinity) {
18924 this.startDate = this.parseDate(this.startDate);
18927 this.updateNavArrows();
18930 setEndDate: function(endDate)
18932 this.endDate = endDate || Infinity;
18933 if (this.endDate !== Infinity) {
18934 this.endDate = this.parseDate(this.endDate);
18937 this.updateNavArrows();
18940 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18942 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18943 if (typeof(this.daysOfWeekDisabled) !== 'object') {
18944 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18946 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18947 return parseInt(d, 10);
18950 this.updateNavArrows();
18953 updateNavArrows: function()
18955 if(this.singleMode){
18959 var d = new Date(this.viewDate),
18960 year = d.getUTCFullYear(),
18961 month = d.getUTCMonth();
18963 Roo.each(this.picker().select('.prev', true).elements, function(v){
18965 switch (this.viewMode) {
18968 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18974 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18981 Roo.each(this.picker().select('.next', true).elements, function(v){
18983 switch (this.viewMode) {
18986 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18992 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19000 moveMonth: function(date, dir)
19005 var new_date = new Date(date.valueOf()),
19006 day = new_date.getUTCDate(),
19007 month = new_date.getUTCMonth(),
19008 mag = Math.abs(dir),
19010 dir = dir > 0 ? 1 : -1;
19013 // If going back one month, make sure month is not current month
19014 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19016 return new_date.getUTCMonth() == month;
19018 // If going forward one month, make sure month is as expected
19019 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19021 return new_date.getUTCMonth() != new_month;
19023 new_month = month + dir;
19024 new_date.setUTCMonth(new_month);
19025 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19026 if (new_month < 0 || new_month > 11) {
19027 new_month = (new_month + 12) % 12;
19030 // For magnitudes >1, move one month at a time...
19031 for (var i=0; i<mag; i++) {
19032 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19033 new_date = this.moveMonth(new_date, dir);
19035 // ...then reset the day, keeping it in the new month
19036 new_month = new_date.getUTCMonth();
19037 new_date.setUTCDate(day);
19039 return new_month != new_date.getUTCMonth();
19042 // Common date-resetting loop -- if date is beyond end of month, make it
19045 new_date.setUTCDate(--day);
19046 new_date.setUTCMonth(new_month);
19051 moveYear: function(date, dir)
19053 return this.moveMonth(date, dir*12);
19056 dateWithinRange: function(date)
19058 return date >= this.startDate && date <= this.endDate;
19064 this.picker().remove();
19067 validateValue : function(value)
19069 if(value.length < 1) {
19070 if(this.allowBlank){
19076 if(value.length < this.minLength){
19079 if(value.length > this.maxLength){
19083 var vt = Roo.form.VTypes;
19084 if(!vt[this.vtype](value, this)){
19088 if(typeof this.validator == "function"){
19089 var msg = this.validator(value);
19095 if(this.regex && !this.regex.test(value)){
19099 if(typeof(this.parseDate(value)) == 'undefined'){
19103 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19107 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19117 Roo.apply(Roo.bootstrap.DateField, {
19128 html: '<i class="fa fa-arrow-left"/>'
19138 html: '<i class="fa fa-arrow-right"/>'
19180 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19181 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19182 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19183 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19184 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19197 navFnc: 'FullYear',
19202 navFnc: 'FullYear',
19207 Roo.apply(Roo.bootstrap.DateField, {
19211 cls: 'datepicker dropdown-menu roo-dynamic',
19215 cls: 'datepicker-days',
19219 cls: 'table-condensed',
19221 Roo.bootstrap.DateField.head,
19225 Roo.bootstrap.DateField.footer
19232 cls: 'datepicker-months',
19236 cls: 'table-condensed',
19238 Roo.bootstrap.DateField.head,
19239 Roo.bootstrap.DateField.content,
19240 Roo.bootstrap.DateField.footer
19247 cls: 'datepicker-years',
19251 cls: 'table-condensed',
19253 Roo.bootstrap.DateField.head,
19254 Roo.bootstrap.DateField.content,
19255 Roo.bootstrap.DateField.footer
19274 * @class Roo.bootstrap.TimeField
19275 * @extends Roo.bootstrap.Input
19276 * Bootstrap DateField class
19280 * Create a new TimeField
19281 * @param {Object} config The config object
19284 Roo.bootstrap.TimeField = function(config){
19285 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19289 * Fires when this field show.
19290 * @param {Roo.bootstrap.DateField} thisthis
19291 * @param {Mixed} date The date value
19296 * Fires when this field hide.
19297 * @param {Roo.bootstrap.DateField} this
19298 * @param {Mixed} date The date value
19303 * Fires when select a date.
19304 * @param {Roo.bootstrap.DateField} this
19305 * @param {Mixed} date The date value
19311 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
19314 * @cfg {String} format
19315 * The default time format string which can be overriden for localization support. The format must be
19316 * valid according to {@link Date#parseDate} (defaults to 'H:i').
19320 onRender: function(ct, position)
19323 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19325 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19327 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19329 this.pop = this.picker().select('>.datepicker-time',true).first();
19330 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19332 this.picker().on('mousedown', this.onMousedown, this);
19333 this.picker().on('click', this.onClick, this);
19335 this.picker().addClass('datepicker-dropdown');
19340 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19341 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19342 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19343 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19344 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19345 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19349 fireKey: function(e){
19350 if (!this.picker().isVisible()){
19351 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19357 e.preventDefault();
19365 this.onTogglePeriod();
19368 this.onIncrementMinutes();
19371 this.onDecrementMinutes();
19380 onClick: function(e) {
19381 e.stopPropagation();
19382 e.preventDefault();
19385 picker : function()
19387 return this.el.select('.datepicker', true).first();
19390 fillTime: function()
19392 var time = this.pop.select('tbody', true).first();
19394 time.dom.innerHTML = '';
19409 cls: 'hours-up glyphicon glyphicon-chevron-up'
19429 cls: 'minutes-up glyphicon glyphicon-chevron-up'
19450 cls: 'timepicker-hour',
19465 cls: 'timepicker-minute',
19480 cls: 'btn btn-primary period',
19502 cls: 'hours-down glyphicon glyphicon-chevron-down'
19522 cls: 'minutes-down glyphicon glyphicon-chevron-down'
19540 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19547 var hours = this.time.getHours();
19548 var minutes = this.time.getMinutes();
19561 hours = hours - 12;
19565 hours = '0' + hours;
19569 minutes = '0' + minutes;
19572 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19573 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19574 this.pop.select('button', true).first().dom.innerHTML = period;
19580 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19582 var cls = ['bottom'];
19584 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19591 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19596 this.picker().addClass(cls.join('-'));
19600 Roo.each(cls, function(c){
19602 _this.picker().setTop(_this.inputEl().getHeight());
19606 _this.picker().setTop(0 - _this.picker().getHeight());
19611 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19615 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19622 onFocus : function()
19624 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19628 onBlur : function()
19630 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19636 this.picker().show();
19641 this.fireEvent('show', this, this.date);
19646 this.picker().hide();
19649 this.fireEvent('hide', this, this.date);
19652 setTime : function()
19655 this.setValue(this.time.format(this.format));
19657 this.fireEvent('select', this, this.date);
19662 onMousedown: function(e){
19663 e.stopPropagation();
19664 e.preventDefault();
19667 onIncrementHours: function()
19669 Roo.log('onIncrementHours');
19670 this.time = this.time.add(Date.HOUR, 1);
19675 onDecrementHours: function()
19677 Roo.log('onDecrementHours');
19678 this.time = this.time.add(Date.HOUR, -1);
19682 onIncrementMinutes: function()
19684 Roo.log('onIncrementMinutes');
19685 this.time = this.time.add(Date.MINUTE, 1);
19689 onDecrementMinutes: function()
19691 Roo.log('onDecrementMinutes');
19692 this.time = this.time.add(Date.MINUTE, -1);
19696 onTogglePeriod: function()
19698 Roo.log('onTogglePeriod');
19699 this.time = this.time.add(Date.HOUR, 12);
19706 Roo.apply(Roo.bootstrap.TimeField, {
19736 cls: 'btn btn-info ok',
19748 Roo.apply(Roo.bootstrap.TimeField, {
19752 cls: 'datepicker dropdown-menu',
19756 cls: 'datepicker-time',
19760 cls: 'table-condensed',
19762 Roo.bootstrap.TimeField.content,
19763 Roo.bootstrap.TimeField.footer
19782 * @class Roo.bootstrap.MonthField
19783 * @extends Roo.bootstrap.Input
19784 * Bootstrap MonthField class
19786 * @cfg {String} language default en
19789 * Create a new MonthField
19790 * @param {Object} config The config object
19793 Roo.bootstrap.MonthField = function(config){
19794 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19799 * Fires when this field show.
19800 * @param {Roo.bootstrap.MonthField} this
19801 * @param {Mixed} date The date value
19806 * Fires when this field hide.
19807 * @param {Roo.bootstrap.MonthField} this
19808 * @param {Mixed} date The date value
19813 * Fires when select a date.
19814 * @param {Roo.bootstrap.MonthField} this
19815 * @param {String} oldvalue The old value
19816 * @param {String} newvalue The new value
19822 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
19824 onRender: function(ct, position)
19827 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19829 this.language = this.language || 'en';
19830 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19831 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19833 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19834 this.isInline = false;
19835 this.isInput = true;
19836 this.component = this.el.select('.add-on', true).first() || false;
19837 this.component = (this.component && this.component.length === 0) ? false : this.component;
19838 this.hasInput = this.component && this.inputEL().length;
19840 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19842 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19844 this.picker().on('mousedown', this.onMousedown, this);
19845 this.picker().on('click', this.onClick, this);
19847 this.picker().addClass('datepicker-dropdown');
19849 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19850 v.setStyle('width', '189px');
19857 if(this.isInline) {
19863 setValue: function(v, suppressEvent)
19865 var o = this.getValue();
19867 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
19871 if(suppressEvent !== true){
19872 this.fireEvent('select', this, o, v);
19877 getValue: function()
19882 onClick: function(e)
19884 e.stopPropagation();
19885 e.preventDefault();
19887 var target = e.getTarget();
19889 if(target.nodeName.toLowerCase() === 'i'){
19890 target = Roo.get(target).dom.parentNode;
19893 var nodeName = target.nodeName;
19894 var className = target.className;
19895 var html = target.innerHTML;
19897 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
19901 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
19903 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19909 picker : function()
19911 return this.pickerEl;
19914 fillMonths: function()
19917 var months = this.picker().select('>.datepicker-months td', true).first();
19919 months.dom.innerHTML = '';
19925 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
19928 months.createChild(month);
19937 if(typeof(this.vIndex) == 'undefined' && this.value.length){
19938 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19941 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19942 e.removeClass('active');
19944 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19945 e.addClass('active');
19952 if(this.isInline) {
19956 this.picker().removeClass(['bottom', 'top']);
19958 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19960 * place to the top of element!
19964 this.picker().addClass('top');
19965 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19970 this.picker().addClass('bottom');
19972 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19975 onFocus : function()
19977 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19981 onBlur : function()
19983 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19985 var d = this.inputEl().getValue();
19994 this.picker().show();
19995 this.picker().select('>.datepicker-months', true).first().show();
19999 this.fireEvent('show', this, this.date);
20004 if(this.isInline) {
20007 this.picker().hide();
20008 this.fireEvent('hide', this, this.date);
20012 onMousedown: function(e)
20014 e.stopPropagation();
20015 e.preventDefault();
20020 Roo.bootstrap.MonthField.superclass.keyup.call(this);
20024 fireKey: function(e)
20026 if (!this.picker().isVisible()){
20027 if (e.keyCode == 27) {// allow escape to hide and re-show picker
20038 e.preventDefault();
20042 dir = e.keyCode == 37 ? -1 : 1;
20044 this.vIndex = this.vIndex + dir;
20046 if(this.vIndex < 0){
20050 if(this.vIndex > 11){
20054 if(isNaN(this.vIndex)){
20058 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20064 dir = e.keyCode == 38 ? -1 : 1;
20066 this.vIndex = this.vIndex + dir * 4;
20068 if(this.vIndex < 0){
20072 if(this.vIndex > 11){
20076 if(isNaN(this.vIndex)){
20080 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20085 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20086 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20090 e.preventDefault();
20093 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20094 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20110 this.picker().remove();
20115 Roo.apply(Roo.bootstrap.MonthField, {
20134 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20135 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20140 Roo.apply(Roo.bootstrap.MonthField, {
20144 cls: 'datepicker dropdown-menu roo-dynamic',
20148 cls: 'datepicker-months',
20152 cls: 'table-condensed',
20154 Roo.bootstrap.DateField.content
20174 * @class Roo.bootstrap.CheckBox
20175 * @extends Roo.bootstrap.Input
20176 * Bootstrap CheckBox class
20178 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20179 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20180 * @cfg {String} boxLabel The text that appears beside the checkbox
20181 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20182 * @cfg {Boolean} checked initnal the element
20183 * @cfg {Boolean} inline inline the element (default false)
20184 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20185 * @cfg {String} tooltip label tooltip
20188 * Create a new CheckBox
20189 * @param {Object} config The config object
20192 Roo.bootstrap.CheckBox = function(config){
20193 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20198 * Fires when the element is checked or unchecked.
20199 * @param {Roo.bootstrap.CheckBox} this This input
20200 * @param {Boolean} checked The new checked value
20205 * Fires when the element is click.
20206 * @param {Roo.bootstrap.CheckBox} this This input
20213 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
20215 inputType: 'checkbox',
20224 getAutoCreate : function()
20226 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20232 cfg.cls = 'form-group ' + this.inputType; //input-group
20235 cfg.cls += ' ' + this.inputType + '-inline';
20241 type : this.inputType,
20242 value : this.inputValue,
20243 cls : 'roo-' + this.inputType, //'form-box',
20244 placeholder : this.placeholder || ''
20248 if(this.inputType != 'radio'){
20252 cls : 'roo-hidden-value',
20253 value : this.checked ? this.inputValue : this.valueOff
20258 if (this.weight) { // Validity check?
20259 cfg.cls += " " + this.inputType + "-" + this.weight;
20262 if (this.disabled) {
20263 input.disabled=true;
20267 input.checked = this.checked;
20272 input.name = this.name;
20274 if(this.inputType != 'radio'){
20275 hidden.name = this.name;
20276 input.name = '_hidden_' + this.name;
20281 input.cls += ' input-' + this.size;
20286 ['xs','sm','md','lg'].map(function(size){
20287 if (settings[size]) {
20288 cfg.cls += ' col-' + size + '-' + settings[size];
20292 var inputblock = input;
20294 if (this.before || this.after) {
20297 cls : 'input-group',
20302 inputblock.cn.push({
20304 cls : 'input-group-addon',
20309 inputblock.cn.push(input);
20311 if(this.inputType != 'radio'){
20312 inputblock.cn.push(hidden);
20316 inputblock.cn.push({
20318 cls : 'input-group-addon',
20325 if (align ==='left' && this.fieldLabel.length) {
20326 // Roo.log("left and has label");
20331 cls : 'control-label',
20332 html : this.fieldLabel
20342 if(this.labelWidth > 12){
20343 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20346 if(this.labelWidth < 13 && this.labelmd == 0){
20347 this.labelmd = this.labelWidth;
20350 if(this.labellg > 0){
20351 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20352 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20355 if(this.labelmd > 0){
20356 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20357 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20360 if(this.labelsm > 0){
20361 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20362 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20365 if(this.labelxs > 0){
20366 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20367 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20370 } else if ( this.fieldLabel.length) {
20371 // Roo.log(" label");
20375 tag: this.boxLabel ? 'span' : 'label',
20377 cls: 'control-label box-input-label',
20378 //cls : 'input-group-addon',
20379 html : this.fieldLabel
20388 // Roo.log(" no label && no align");
20389 cfg.cn = [ inputblock ] ;
20395 var boxLabelCfg = {
20397 //'for': id, // box label is handled by onclick - so no for...
20399 html: this.boxLabel
20403 boxLabelCfg.tooltip = this.tooltip;
20406 cfg.cn.push(boxLabelCfg);
20409 if(this.inputType != 'radio'){
20410 cfg.cn.push(hidden);
20418 * return the real input element.
20420 inputEl: function ()
20422 return this.el.select('input.roo-' + this.inputType,true).first();
20424 hiddenEl: function ()
20426 return this.el.select('input.roo-hidden-value',true).first();
20429 labelEl: function()
20431 return this.el.select('label.control-label',true).first();
20433 /* depricated... */
20437 return this.labelEl();
20440 boxLabelEl: function()
20442 return this.el.select('label.box-label',true).first();
20445 initEvents : function()
20447 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20449 this.inputEl().on('click', this.onClick, this);
20451 if (this.boxLabel) {
20452 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
20455 this.startValue = this.getValue();
20458 Roo.bootstrap.CheckBox.register(this);
20462 onClick : function(e)
20464 if(this.fireEvent('click', this, e) !== false){
20465 this.setChecked(!this.checked);
20470 setChecked : function(state,suppressEvent)
20472 this.startValue = this.getValue();
20474 if(this.inputType == 'radio'){
20476 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20477 e.dom.checked = false;
20480 this.inputEl().dom.checked = true;
20482 this.inputEl().dom.value = this.inputValue;
20484 if(suppressEvent !== true){
20485 this.fireEvent('check', this, true);
20493 this.checked = state;
20495 this.inputEl().dom.checked = state;
20498 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20500 if(suppressEvent !== true){
20501 this.fireEvent('check', this, state);
20507 getValue : function()
20509 if(this.inputType == 'radio'){
20510 return this.getGroupValue();
20513 return this.hiddenEl().dom.value;
20517 getGroupValue : function()
20519 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20523 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20526 setValue : function(v,suppressEvent)
20528 if(this.inputType == 'radio'){
20529 this.setGroupValue(v, suppressEvent);
20533 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20538 setGroupValue : function(v, suppressEvent)
20540 this.startValue = this.getValue();
20542 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20543 e.dom.checked = false;
20545 if(e.dom.value == v){
20546 e.dom.checked = true;
20550 if(suppressEvent !== true){
20551 this.fireEvent('check', this, true);
20559 validate : function()
20563 (this.inputType == 'radio' && this.validateRadio()) ||
20564 (this.inputType == 'checkbox' && this.validateCheckbox())
20570 this.markInvalid();
20574 validateRadio : function()
20576 if(this.allowBlank){
20582 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20583 if(!e.dom.checked){
20595 validateCheckbox : function()
20598 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20599 //return (this.getValue() == this.inputValue) ? true : false;
20602 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20610 for(var i in group){
20611 if(group[i].el.isVisible(true)){
20619 for(var i in group){
20624 r = (group[i].getValue() == group[i].inputValue) ? true : false;
20631 * Mark this field as valid
20633 markValid : function()
20637 this.fireEvent('valid', this);
20639 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20642 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20649 if(this.inputType == 'radio'){
20650 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20651 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20652 e.findParent('.form-group', false, true).addClass(_this.validClass);
20659 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20660 this.el.findParent('.form-group', false, true).addClass(this.validClass);
20664 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20670 for(var i in group){
20671 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20672 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20677 * Mark this field as invalid
20678 * @param {String} msg The validation message
20680 markInvalid : function(msg)
20682 if(this.allowBlank){
20688 this.fireEvent('invalid', this, msg);
20690 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20693 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20697 label.markInvalid();
20700 if(this.inputType == 'radio'){
20701 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20702 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20703 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20710 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20711 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20715 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20721 for(var i in group){
20722 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20723 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20728 clearInvalid : function()
20730 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20732 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20734 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20736 if (label && label.iconEl) {
20737 label.iconEl.removeClass(label.validClass);
20738 label.iconEl.removeClass(label.invalidClass);
20742 disable : function()
20744 if(this.inputType != 'radio'){
20745 Roo.bootstrap.CheckBox.superclass.disable.call(this);
20752 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20753 _this.getActionEl().addClass(this.disabledClass);
20754 e.dom.disabled = true;
20758 this.disabled = true;
20759 this.fireEvent("disable", this);
20763 enable : function()
20765 if(this.inputType != 'radio'){
20766 Roo.bootstrap.CheckBox.superclass.enable.call(this);
20773 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20774 _this.getActionEl().removeClass(this.disabledClass);
20775 e.dom.disabled = false;
20779 this.disabled = false;
20780 this.fireEvent("enable", this);
20784 setBoxLabel : function(v)
20789 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
20795 Roo.apply(Roo.bootstrap.CheckBox, {
20800 * register a CheckBox Group
20801 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20803 register : function(checkbox)
20805 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20806 this.groups[checkbox.groupId] = {};
20809 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20813 this.groups[checkbox.groupId][checkbox.name] = checkbox;
20817 * fetch a CheckBox Group based on the group ID
20818 * @param {string} the group ID
20819 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20821 get: function(groupId) {
20822 if (typeof(this.groups[groupId]) == 'undefined') {
20826 return this.groups[groupId] ;
20839 * @class Roo.bootstrap.Radio
20840 * @extends Roo.bootstrap.Component
20841 * Bootstrap Radio class
20842 * @cfg {String} boxLabel - the label associated
20843 * @cfg {String} value - the value of radio
20846 * Create a new Radio
20847 * @param {Object} config The config object
20849 Roo.bootstrap.Radio = function(config){
20850 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
20854 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
20860 getAutoCreate : function()
20864 cls : 'form-group radio',
20869 html : this.boxLabel
20877 initEvents : function()
20879 this.parent().register(this);
20881 this.el.on('click', this.onClick, this);
20885 onClick : function()
20887 this.setChecked(true);
20890 setChecked : function(state, suppressEvent)
20892 this.parent().setValue(this.value, suppressEvent);
20896 setBoxLabel : function(v)
20901 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
20916 * @class Roo.bootstrap.SecurePass
20917 * @extends Roo.bootstrap.Input
20918 * Bootstrap SecurePass class
20922 * Create a new SecurePass
20923 * @param {Object} config The config object
20926 Roo.bootstrap.SecurePass = function (config) {
20927 // these go here, so the translation tool can replace them..
20929 PwdEmpty: "Please type a password, and then retype it to confirm.",
20930 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20931 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20932 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20933 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20934 FNInPwd: "Your password can't contain your first name. Please type a different password.",
20935 LNInPwd: "Your password can't contain your last name. Please type a different password.",
20936 TooWeak: "Your password is Too Weak."
20938 this.meterLabel = "Password strength:";
20939 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
20940 this.meterClass = [
20941 "roo-password-meter-tooweak",
20942 "roo-password-meter-weak",
20943 "roo-password-meter-medium",
20944 "roo-password-meter-strong",
20945 "roo-password-meter-grey"
20950 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
20953 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
20955 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
20957 * PwdEmpty: "Please type a password, and then retype it to confirm.",
20958 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20959 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20960 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20961 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20962 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
20963 * LNInPwd: "Your password can't contain your last name. Please type a different password."
20973 * @cfg {String/Object} Label for the strength meter (defaults to
20974 * 'Password strength:')
20979 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
20980 * ['Weak', 'Medium', 'Strong'])
20983 pwdStrengths: false,
20996 initEvents: function ()
20998 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21000 if (this.el.is('input[type=password]') && Roo.isSafari) {
21001 this.el.on('keydown', this.SafariOnKeyDown, this);
21004 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21007 onRender: function (ct, position)
21009 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21010 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21011 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21013 this.trigger.createChild({
21018 cls: 'roo-password-meter-grey col-xs-12',
21021 //width: this.meterWidth + 'px'
21025 cls: 'roo-password-meter-text'
21031 if (this.hideTrigger) {
21032 this.trigger.setDisplayed(false);
21034 this.setSize(this.width || '', this.height || '');
21037 onDestroy: function ()
21039 if (this.trigger) {
21040 this.trigger.removeAllListeners();
21041 this.trigger.remove();
21044 this.wrap.remove();
21046 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21049 checkStrength: function ()
21051 var pwd = this.inputEl().getValue();
21052 if (pwd == this._lastPwd) {
21057 if (this.ClientSideStrongPassword(pwd)) {
21059 } else if (this.ClientSideMediumPassword(pwd)) {
21061 } else if (this.ClientSideWeakPassword(pwd)) {
21067 Roo.log('strength1: ' + strength);
21069 //var pm = this.trigger.child('div/div/div').dom;
21070 var pm = this.trigger.child('div/div');
21071 pm.removeClass(this.meterClass);
21072 pm.addClass(this.meterClass[strength]);
21075 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21077 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21079 this._lastPwd = pwd;
21083 Roo.bootstrap.SecurePass.superclass.reset.call(this);
21085 this._lastPwd = '';
21087 var pm = this.trigger.child('div/div');
21088 pm.removeClass(this.meterClass);
21089 pm.addClass('roo-password-meter-grey');
21092 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21095 this.inputEl().dom.type='password';
21098 validateValue: function (value)
21101 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21104 if (value.length == 0) {
21105 if (this.allowBlank) {
21106 this.clearInvalid();
21110 this.markInvalid(this.errors.PwdEmpty);
21111 this.errorMsg = this.errors.PwdEmpty;
21119 if ('[\x21-\x7e]*'.match(value)) {
21120 this.markInvalid(this.errors.PwdBadChar);
21121 this.errorMsg = this.errors.PwdBadChar;
21124 if (value.length < 6) {
21125 this.markInvalid(this.errors.PwdShort);
21126 this.errorMsg = this.errors.PwdShort;
21129 if (value.length > 16) {
21130 this.markInvalid(this.errors.PwdLong);
21131 this.errorMsg = this.errors.PwdLong;
21135 if (this.ClientSideStrongPassword(value)) {
21137 } else if (this.ClientSideMediumPassword(value)) {
21139 } else if (this.ClientSideWeakPassword(value)) {
21146 if (strength < 2) {
21147 //this.markInvalid(this.errors.TooWeak);
21148 this.errorMsg = this.errors.TooWeak;
21153 console.log('strength2: ' + strength);
21155 //var pm = this.trigger.child('div/div/div').dom;
21157 var pm = this.trigger.child('div/div');
21158 pm.removeClass(this.meterClass);
21159 pm.addClass(this.meterClass[strength]);
21161 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21163 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21165 this.errorMsg = '';
21169 CharacterSetChecks: function (type)
21172 this.fResult = false;
21175 isctype: function (character, type)
21178 case this.kCapitalLetter:
21179 if (character >= 'A' && character <= 'Z') {
21184 case this.kSmallLetter:
21185 if (character >= 'a' && character <= 'z') {
21191 if (character >= '0' && character <= '9') {
21196 case this.kPunctuation:
21197 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21208 IsLongEnough: function (pwd, size)
21210 return !(pwd == null || isNaN(size) || pwd.length < size);
21213 SpansEnoughCharacterSets: function (word, nb)
21215 if (!this.IsLongEnough(word, nb))
21220 var characterSetChecks = new Array(
21221 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21222 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21225 for (var index = 0; index < word.length; ++index) {
21226 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21227 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21228 characterSetChecks[nCharSet].fResult = true;
21235 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21236 if (characterSetChecks[nCharSet].fResult) {
21241 if (nCharSets < nb) {
21247 ClientSideStrongPassword: function (pwd)
21249 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21252 ClientSideMediumPassword: function (pwd)
21254 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21257 ClientSideWeakPassword: function (pwd)
21259 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21262 })//<script type="text/javascript">
21265 * Based Ext JS Library 1.1.1
21266 * Copyright(c) 2006-2007, Ext JS, LLC.
21272 * @class Roo.HtmlEditorCore
21273 * @extends Roo.Component
21274 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21276 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21279 Roo.HtmlEditorCore = function(config){
21282 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21287 * @event initialize
21288 * Fires when the editor is fully initialized (including the iframe)
21289 * @param {Roo.HtmlEditorCore} this
21294 * Fires when the editor is first receives the focus. Any insertion must wait
21295 * until after this event.
21296 * @param {Roo.HtmlEditorCore} this
21300 * @event beforesync
21301 * Fires before the textarea is updated with content from the editor iframe. Return false
21302 * to cancel the sync.
21303 * @param {Roo.HtmlEditorCore} this
21304 * @param {String} html
21308 * @event beforepush
21309 * Fires before the iframe editor is updated with content from the textarea. Return false
21310 * to cancel the push.
21311 * @param {Roo.HtmlEditorCore} this
21312 * @param {String} html
21317 * Fires when the textarea is updated with content from the editor iframe.
21318 * @param {Roo.HtmlEditorCore} this
21319 * @param {String} html
21324 * Fires when the iframe editor is updated with content from the textarea.
21325 * @param {Roo.HtmlEditorCore} this
21326 * @param {String} html
21331 * @event editorevent
21332 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21333 * @param {Roo.HtmlEditorCore} this
21339 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21341 // defaults : white / black...
21342 this.applyBlacklists();
21349 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
21353 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
21359 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21364 * @cfg {Number} height (in pixels)
21368 * @cfg {Number} width (in pixels)
21373 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21376 stylesheets: false,
21381 // private properties
21382 validationEvent : false,
21384 initialized : false,
21386 sourceEditMode : false,
21387 onFocus : Roo.emptyFn,
21389 hideMode:'offsets',
21393 // blacklist + whitelisted elements..
21400 * Protected method that will not generally be called directly. It
21401 * is called when the editor initializes the iframe with HTML contents. Override this method if you
21402 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21404 getDocMarkup : function(){
21408 // inherit styels from page...??
21409 if (this.stylesheets === false) {
21411 Roo.get(document.head).select('style').each(function(node) {
21412 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21415 Roo.get(document.head).select('link').each(function(node) {
21416 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21419 } else if (!this.stylesheets.length) {
21421 st = '<style type="text/css">' +
21422 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21425 st = '<style type="text/css">' +
21430 st += '<style type="text/css">' +
21431 'IMG { cursor: pointer } ' +
21434 var cls = 'roo-htmleditor-body';
21436 if(this.bodyCls.length){
21437 cls += ' ' + this.bodyCls;
21440 return '<html><head>' + st +
21441 //<style type="text/css">' +
21442 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21444 ' </head><body class="' + cls + '"></body></html>';
21448 onRender : function(ct, position)
21451 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21452 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21455 this.el.dom.style.border = '0 none';
21456 this.el.dom.setAttribute('tabIndex', -1);
21457 this.el.addClass('x-hidden hide');
21461 if(Roo.isIE){ // fix IE 1px bogus margin
21462 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21466 this.frameId = Roo.id();
21470 var iframe = this.owner.wrap.createChild({
21472 cls: 'form-control', // bootstrap..
21474 name: this.frameId,
21475 frameBorder : 'no',
21476 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
21481 this.iframe = iframe.dom;
21483 this.assignDocWin();
21485 this.doc.designMode = 'on';
21488 this.doc.write(this.getDocMarkup());
21492 var task = { // must defer to wait for browser to be ready
21494 //console.log("run task?" + this.doc.readyState);
21495 this.assignDocWin();
21496 if(this.doc.body || this.doc.readyState == 'complete'){
21498 this.doc.designMode="on";
21502 Roo.TaskMgr.stop(task);
21503 this.initEditor.defer(10, this);
21510 Roo.TaskMgr.start(task);
21515 onResize : function(w, h)
21517 Roo.log('resize: ' +w + ',' + h );
21518 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21522 if(typeof w == 'number'){
21524 this.iframe.style.width = w + 'px';
21526 if(typeof h == 'number'){
21528 this.iframe.style.height = h + 'px';
21530 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21537 * Toggles the editor between standard and source edit mode.
21538 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21540 toggleSourceEdit : function(sourceEditMode){
21542 this.sourceEditMode = sourceEditMode === true;
21544 if(this.sourceEditMode){
21546 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
21549 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21550 //this.iframe.className = '';
21553 //this.setSize(this.owner.wrap.getSize());
21554 //this.fireEvent('editmodechange', this, this.sourceEditMode);
21561 * Protected method that will not generally be called directly. If you need/want
21562 * custom HTML cleanup, this is the method you should override.
21563 * @param {String} html The HTML to be cleaned
21564 * return {String} The cleaned HTML
21566 cleanHtml : function(html){
21567 html = String(html);
21568 if(html.length > 5){
21569 if(Roo.isSafari){ // strip safari nonsense
21570 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21573 if(html == ' '){
21580 * HTML Editor -> Textarea
21581 * Protected method that will not generally be called directly. Syncs the contents
21582 * of the editor iframe with the textarea.
21584 syncValue : function(){
21585 if(this.initialized){
21586 var bd = (this.doc.body || this.doc.documentElement);
21587 //this.cleanUpPaste(); -- this is done else where and causes havoc..
21588 var html = bd.innerHTML;
21590 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21591 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21593 html = '<div style="'+m[0]+'">' + html + '</div>';
21596 html = this.cleanHtml(html);
21597 // fix up the special chars.. normaly like back quotes in word...
21598 // however we do not want to do this with chinese..
21599 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21600 var cc = b.charCodeAt();
21602 (cc >= 0x4E00 && cc < 0xA000 ) ||
21603 (cc >= 0x3400 && cc < 0x4E00 ) ||
21604 (cc >= 0xf900 && cc < 0xfb00 )
21610 if(this.owner.fireEvent('beforesync', this, html) !== false){
21611 this.el.dom.value = html;
21612 this.owner.fireEvent('sync', this, html);
21618 * Protected method that will not generally be called directly. Pushes the value of the textarea
21619 * into the iframe editor.
21621 pushValue : function(){
21622 if(this.initialized){
21623 var v = this.el.dom.value.trim();
21625 // if(v.length < 1){
21629 if(this.owner.fireEvent('beforepush', this, v) !== false){
21630 var d = (this.doc.body || this.doc.documentElement);
21632 this.cleanUpPaste();
21633 this.el.dom.value = d.innerHTML;
21634 this.owner.fireEvent('push', this, v);
21640 deferFocus : function(){
21641 this.focus.defer(10, this);
21645 focus : function(){
21646 if(this.win && !this.sourceEditMode){
21653 assignDocWin: function()
21655 var iframe = this.iframe;
21658 this.doc = iframe.contentWindow.document;
21659 this.win = iframe.contentWindow;
21661 // if (!Roo.get(this.frameId)) {
21664 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21665 // this.win = Roo.get(this.frameId).dom.contentWindow;
21667 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21671 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21672 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21677 initEditor : function(){
21678 //console.log("INIT EDITOR");
21679 this.assignDocWin();
21683 this.doc.designMode="on";
21685 this.doc.write(this.getDocMarkup());
21688 var dbody = (this.doc.body || this.doc.documentElement);
21689 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21690 // this copies styles from the containing element into thsi one..
21691 // not sure why we need all of this..
21692 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21694 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21695 //ss['background-attachment'] = 'fixed'; // w3c
21696 dbody.bgProperties = 'fixed'; // ie
21697 //Roo.DomHelper.applyStyles(dbody, ss);
21698 Roo.EventManager.on(this.doc, {
21699 //'mousedown': this.onEditorEvent,
21700 'mouseup': this.onEditorEvent,
21701 'dblclick': this.onEditorEvent,
21702 'click': this.onEditorEvent,
21703 'keyup': this.onEditorEvent,
21708 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21710 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21711 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21713 this.initialized = true;
21715 this.owner.fireEvent('initialize', this);
21720 onDestroy : function(){
21726 //for (var i =0; i < this.toolbars.length;i++) {
21727 // // fixme - ask toolbars for heights?
21728 // this.toolbars[i].onDestroy();
21731 //this.wrap.dom.innerHTML = '';
21732 //this.wrap.remove();
21737 onFirstFocus : function(){
21739 this.assignDocWin();
21742 this.activated = true;
21745 if(Roo.isGecko){ // prevent silly gecko errors
21747 var s = this.win.getSelection();
21748 if(!s.focusNode || s.focusNode.nodeType != 3){
21749 var r = s.getRangeAt(0);
21750 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21755 this.execCmd('useCSS', true);
21756 this.execCmd('styleWithCSS', false);
21759 this.owner.fireEvent('activate', this);
21763 adjustFont: function(btn){
21764 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21765 //if(Roo.isSafari){ // safari
21768 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21769 if(Roo.isSafari){ // safari
21770 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21771 v = (v < 10) ? 10 : v;
21772 v = (v > 48) ? 48 : v;
21773 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21778 v = Math.max(1, v+adjust);
21780 this.execCmd('FontSize', v );
21783 onEditorEvent : function(e)
21785 this.owner.fireEvent('editorevent', this, e);
21786 // this.updateToolbar();
21787 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21790 insertTag : function(tg)
21792 // could be a bit smarter... -> wrap the current selected tRoo..
21793 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
21795 range = this.createRange(this.getSelection());
21796 var wrappingNode = this.doc.createElement(tg.toLowerCase());
21797 wrappingNode.appendChild(range.extractContents());
21798 range.insertNode(wrappingNode);
21805 this.execCmd("formatblock", tg);
21809 insertText : function(txt)
21813 var range = this.createRange();
21814 range.deleteContents();
21815 //alert(Sender.getAttribute('label'));
21817 range.insertNode(this.doc.createTextNode(txt));
21823 * Executes a Midas editor command on the editor document and performs necessary focus and
21824 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21825 * @param {String} cmd The Midas command
21826 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21828 relayCmd : function(cmd, value){
21830 this.execCmd(cmd, value);
21831 this.owner.fireEvent('editorevent', this);
21832 //this.updateToolbar();
21833 this.owner.deferFocus();
21837 * Executes a Midas editor command directly on the editor document.
21838 * For visual commands, you should use {@link #relayCmd} instead.
21839 * <b>This should only be called after the editor is initialized.</b>
21840 * @param {String} cmd The Midas command
21841 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21843 execCmd : function(cmd, value){
21844 this.doc.execCommand(cmd, false, value === undefined ? null : value);
21851 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21853 * @param {String} text | dom node..
21855 insertAtCursor : function(text)
21858 if(!this.activated){
21864 var r = this.doc.selection.createRange();
21875 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21879 // from jquery ui (MIT licenced)
21881 var win = this.win;
21883 if (win.getSelection && win.getSelection().getRangeAt) {
21884 range = win.getSelection().getRangeAt(0);
21885 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21886 range.insertNode(node);
21887 } else if (win.document.selection && win.document.selection.createRange) {
21888 // no firefox support
21889 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21890 win.document.selection.createRange().pasteHTML(txt);
21892 // no firefox support
21893 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21894 this.execCmd('InsertHTML', txt);
21903 mozKeyPress : function(e){
21905 var c = e.getCharCode(), cmd;
21908 c = String.fromCharCode(c).toLowerCase();
21922 this.cleanUpPaste.defer(100, this);
21930 e.preventDefault();
21938 fixKeys : function(){ // load time branching for fastest keydown performance
21940 return function(e){
21941 var k = e.getKey(), r;
21944 r = this.doc.selection.createRange();
21947 r.pasteHTML('    ');
21954 r = this.doc.selection.createRange();
21956 var target = r.parentElement();
21957 if(!target || target.tagName.toLowerCase() != 'li'){
21959 r.pasteHTML('<br />');
21965 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21966 this.cleanUpPaste.defer(100, this);
21972 }else if(Roo.isOpera){
21973 return function(e){
21974 var k = e.getKey();
21978 this.execCmd('InsertHTML','    ');
21981 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21982 this.cleanUpPaste.defer(100, this);
21987 }else if(Roo.isSafari){
21988 return function(e){
21989 var k = e.getKey();
21993 this.execCmd('InsertText','\t');
21997 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21998 this.cleanUpPaste.defer(100, this);
22006 getAllAncestors: function()
22008 var p = this.getSelectedNode();
22011 a.push(p); // push blank onto stack..
22012 p = this.getParentElement();
22016 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22020 a.push(this.doc.body);
22024 lastSelNode : false,
22027 getSelection : function()
22029 this.assignDocWin();
22030 return Roo.isIE ? this.doc.selection : this.win.getSelection();
22033 getSelectedNode: function()
22035 // this may only work on Gecko!!!
22037 // should we cache this!!!!
22042 var range = this.createRange(this.getSelection()).cloneRange();
22045 var parent = range.parentElement();
22047 var testRange = range.duplicate();
22048 testRange.moveToElementText(parent);
22049 if (testRange.inRange(range)) {
22052 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22055 parent = parent.parentElement;
22060 // is ancestor a text element.
22061 var ac = range.commonAncestorContainer;
22062 if (ac.nodeType == 3) {
22063 ac = ac.parentNode;
22066 var ar = ac.childNodes;
22069 var other_nodes = [];
22070 var has_other_nodes = false;
22071 for (var i=0;i<ar.length;i++) {
22072 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
22075 // fullly contained node.
22077 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22082 // probably selected..
22083 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22084 other_nodes.push(ar[i]);
22088 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
22093 has_other_nodes = true;
22095 if (!nodes.length && other_nodes.length) {
22096 nodes= other_nodes;
22098 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22104 createRange: function(sel)
22106 // this has strange effects when using with
22107 // top toolbar - not sure if it's a great idea.
22108 //this.editor.contentWindow.focus();
22109 if (typeof sel != "undefined") {
22111 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22113 return this.doc.createRange();
22116 return this.doc.createRange();
22119 getParentElement: function()
22122 this.assignDocWin();
22123 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22125 var range = this.createRange(sel);
22128 var p = range.commonAncestorContainer;
22129 while (p.nodeType == 3) { // text node
22140 * Range intersection.. the hard stuff...
22144 * [ -- selected range --- ]
22148 * if end is before start or hits it. fail.
22149 * if start is after end or hits it fail.
22151 * if either hits (but other is outside. - then it's not
22157 // @see http://www.thismuchiknow.co.uk/?p=64.
22158 rangeIntersectsNode : function(range, node)
22160 var nodeRange = node.ownerDocument.createRange();
22162 nodeRange.selectNode(node);
22164 nodeRange.selectNodeContents(node);
22167 var rangeStartRange = range.cloneRange();
22168 rangeStartRange.collapse(true);
22170 var rangeEndRange = range.cloneRange();
22171 rangeEndRange.collapse(false);
22173 var nodeStartRange = nodeRange.cloneRange();
22174 nodeStartRange.collapse(true);
22176 var nodeEndRange = nodeRange.cloneRange();
22177 nodeEndRange.collapse(false);
22179 return rangeStartRange.compareBoundaryPoints(
22180 Range.START_TO_START, nodeEndRange) == -1 &&
22181 rangeEndRange.compareBoundaryPoints(
22182 Range.START_TO_START, nodeStartRange) == 1;
22186 rangeCompareNode : function(range, node)
22188 var nodeRange = node.ownerDocument.createRange();
22190 nodeRange.selectNode(node);
22192 nodeRange.selectNodeContents(node);
22196 range.collapse(true);
22198 nodeRange.collapse(true);
22200 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22201 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
22203 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22205 var nodeIsBefore = ss == 1;
22206 var nodeIsAfter = ee == -1;
22208 if (nodeIsBefore && nodeIsAfter) {
22211 if (!nodeIsBefore && nodeIsAfter) {
22212 return 1; //right trailed.
22215 if (nodeIsBefore && !nodeIsAfter) {
22216 return 2; // left trailed.
22222 // private? - in a new class?
22223 cleanUpPaste : function()
22225 // cleans up the whole document..
22226 Roo.log('cleanuppaste');
22228 this.cleanUpChildren(this.doc.body);
22229 var clean = this.cleanWordChars(this.doc.body.innerHTML);
22230 if (clean != this.doc.body.innerHTML) {
22231 this.doc.body.innerHTML = clean;
22236 cleanWordChars : function(input) {// change the chars to hex code
22237 var he = Roo.HtmlEditorCore;
22239 var output = input;
22240 Roo.each(he.swapCodes, function(sw) {
22241 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22243 output = output.replace(swapper, sw[1]);
22250 cleanUpChildren : function (n)
22252 if (!n.childNodes.length) {
22255 for (var i = n.childNodes.length-1; i > -1 ; i--) {
22256 this.cleanUpChild(n.childNodes[i]);
22263 cleanUpChild : function (node)
22266 //console.log(node);
22267 if (node.nodeName == "#text") {
22268 // clean up silly Windows -- stuff?
22271 if (node.nodeName == "#comment") {
22272 node.parentNode.removeChild(node);
22273 // clean up silly Windows -- stuff?
22276 var lcname = node.tagName.toLowerCase();
22277 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22278 // whitelist of tags..
22280 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22282 node.parentNode.removeChild(node);
22287 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22289 // remove <a name=....> as rendering on yahoo mailer is borked with this.
22290 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22292 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22293 // remove_keep_children = true;
22296 if (remove_keep_children) {
22297 this.cleanUpChildren(node);
22298 // inserts everything just before this node...
22299 while (node.childNodes.length) {
22300 var cn = node.childNodes[0];
22301 node.removeChild(cn);
22302 node.parentNode.insertBefore(cn, node);
22304 node.parentNode.removeChild(node);
22308 if (!node.attributes || !node.attributes.length) {
22309 this.cleanUpChildren(node);
22313 function cleanAttr(n,v)
22316 if (v.match(/^\./) || v.match(/^\//)) {
22319 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
22322 if (v.match(/^#/)) {
22325 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22326 node.removeAttribute(n);
22330 var cwhite = this.cwhite;
22331 var cblack = this.cblack;
22333 function cleanStyle(n,v)
22335 if (v.match(/expression/)) { //XSS?? should we even bother..
22336 node.removeAttribute(n);
22340 var parts = v.split(/;/);
22343 Roo.each(parts, function(p) {
22344 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22348 var l = p.split(':').shift().replace(/\s+/g,'');
22349 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22351 if ( cwhite.length && cblack.indexOf(l) > -1) {
22352 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22353 //node.removeAttribute(n);
22357 // only allow 'c whitelisted system attributes'
22358 if ( cwhite.length && cwhite.indexOf(l) < 0) {
22359 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22360 //node.removeAttribute(n);
22370 if (clean.length) {
22371 node.setAttribute(n, clean.join(';'));
22373 node.removeAttribute(n);
22379 for (var i = node.attributes.length-1; i > -1 ; i--) {
22380 var a = node.attributes[i];
22383 if (a.name.toLowerCase().substr(0,2)=='on') {
22384 node.removeAttribute(a.name);
22387 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22388 node.removeAttribute(a.name);
22391 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22392 cleanAttr(a.name,a.value); // fixme..
22395 if (a.name == 'style') {
22396 cleanStyle(a.name,a.value);
22399 /// clean up MS crap..
22400 // tecnically this should be a list of valid class'es..
22403 if (a.name == 'class') {
22404 if (a.value.match(/^Mso/)) {
22405 node.className = '';
22408 if (a.value.match(/^body$/)) {
22409 node.className = '';
22420 this.cleanUpChildren(node);
22426 * Clean up MS wordisms...
22428 cleanWord : function(node)
22433 this.cleanWord(this.doc.body);
22436 if (node.nodeName == "#text") {
22437 // clean up silly Windows -- stuff?
22440 if (node.nodeName == "#comment") {
22441 node.parentNode.removeChild(node);
22442 // clean up silly Windows -- stuff?
22446 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22447 node.parentNode.removeChild(node);
22451 // remove - but keep children..
22452 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22453 while (node.childNodes.length) {
22454 var cn = node.childNodes[0];
22455 node.removeChild(cn);
22456 node.parentNode.insertBefore(cn, node);
22458 node.parentNode.removeChild(node);
22459 this.iterateChildren(node, this.cleanWord);
22463 if (node.className.length) {
22465 var cn = node.className.split(/\W+/);
22467 Roo.each(cn, function(cls) {
22468 if (cls.match(/Mso[a-zA-Z]+/)) {
22473 node.className = cna.length ? cna.join(' ') : '';
22475 node.removeAttribute("class");
22479 if (node.hasAttribute("lang")) {
22480 node.removeAttribute("lang");
22483 if (node.hasAttribute("style")) {
22485 var styles = node.getAttribute("style").split(";");
22487 Roo.each(styles, function(s) {
22488 if (!s.match(/:/)) {
22491 var kv = s.split(":");
22492 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22495 // what ever is left... we allow.
22498 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22499 if (!nstyle.length) {
22500 node.removeAttribute('style');
22503 this.iterateChildren(node, this.cleanWord);
22509 * iterateChildren of a Node, calling fn each time, using this as the scole..
22510 * @param {DomNode} node node to iterate children of.
22511 * @param {Function} fn method of this class to call on each item.
22513 iterateChildren : function(node, fn)
22515 if (!node.childNodes.length) {
22518 for (var i = node.childNodes.length-1; i > -1 ; i--) {
22519 fn.call(this, node.childNodes[i])
22525 * cleanTableWidths.
22527 * Quite often pasting from word etc.. results in tables with column and widths.
22528 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22531 cleanTableWidths : function(node)
22536 this.cleanTableWidths(this.doc.body);
22541 if (node.nodeName == "#text" || node.nodeName == "#comment") {
22544 Roo.log(node.tagName);
22545 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22546 this.iterateChildren(node, this.cleanTableWidths);
22549 if (node.hasAttribute('width')) {
22550 node.removeAttribute('width');
22554 if (node.hasAttribute("style")) {
22557 var styles = node.getAttribute("style").split(";");
22559 Roo.each(styles, function(s) {
22560 if (!s.match(/:/)) {
22563 var kv = s.split(":");
22564 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22567 // what ever is left... we allow.
22570 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22571 if (!nstyle.length) {
22572 node.removeAttribute('style');
22576 this.iterateChildren(node, this.cleanTableWidths);
22584 domToHTML : function(currentElement, depth, nopadtext) {
22586 depth = depth || 0;
22587 nopadtext = nopadtext || false;
22589 if (!currentElement) {
22590 return this.domToHTML(this.doc.body);
22593 //Roo.log(currentElement);
22595 var allText = false;
22596 var nodeName = currentElement.nodeName;
22597 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22599 if (nodeName == '#text') {
22601 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22606 if (nodeName != 'BODY') {
22609 // Prints the node tagName, such as <A>, <IMG>, etc
22612 for(i = 0; i < currentElement.attributes.length;i++) {
22614 var aname = currentElement.attributes.item(i).name;
22615 if (!currentElement.attributes.item(i).value.length) {
22618 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22621 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22630 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22633 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22638 // Traverse the tree
22640 var currentElementChild = currentElement.childNodes.item(i);
22641 var allText = true;
22642 var innerHTML = '';
22644 while (currentElementChild) {
22645 // Formatting code (indent the tree so it looks nice on the screen)
22646 var nopad = nopadtext;
22647 if (lastnode == 'SPAN') {
22651 if (currentElementChild.nodeName == '#text') {
22652 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22653 toadd = nopadtext ? toadd : toadd.trim();
22654 if (!nopad && toadd.length > 80) {
22655 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
22657 innerHTML += toadd;
22660 currentElementChild = currentElement.childNodes.item(i);
22666 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
22668 // Recursively traverse the tree structure of the child node
22669 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
22670 lastnode = currentElementChild.nodeName;
22672 currentElementChild=currentElement.childNodes.item(i);
22678 // The remaining code is mostly for formatting the tree
22679 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
22684 ret+= "</"+tagName+">";
22690 applyBlacklists : function()
22692 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
22693 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
22697 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22698 if (b.indexOf(tag) > -1) {
22701 this.white.push(tag);
22705 Roo.each(w, function(tag) {
22706 if (b.indexOf(tag) > -1) {
22709 if (this.white.indexOf(tag) > -1) {
22712 this.white.push(tag);
22717 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22718 if (w.indexOf(tag) > -1) {
22721 this.black.push(tag);
22725 Roo.each(b, function(tag) {
22726 if (w.indexOf(tag) > -1) {
22729 if (this.black.indexOf(tag) > -1) {
22732 this.black.push(tag);
22737 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
22738 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
22742 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22743 if (b.indexOf(tag) > -1) {
22746 this.cwhite.push(tag);
22750 Roo.each(w, function(tag) {
22751 if (b.indexOf(tag) > -1) {
22754 if (this.cwhite.indexOf(tag) > -1) {
22757 this.cwhite.push(tag);
22762 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22763 if (w.indexOf(tag) > -1) {
22766 this.cblack.push(tag);
22770 Roo.each(b, function(tag) {
22771 if (w.indexOf(tag) > -1) {
22774 if (this.cblack.indexOf(tag) > -1) {
22777 this.cblack.push(tag);
22782 setStylesheets : function(stylesheets)
22784 if(typeof(stylesheets) == 'string'){
22785 Roo.get(this.iframe.contentDocument.head).createChild({
22787 rel : 'stylesheet',
22796 Roo.each(stylesheets, function(s) {
22801 Roo.get(_this.iframe.contentDocument.head).createChild({
22803 rel : 'stylesheet',
22812 removeStylesheets : function()
22816 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22821 setStyle : function(style)
22823 Roo.get(this.iframe.contentDocument.head).createChild({
22832 // hide stuff that is not compatible
22846 * @event specialkey
22850 * @cfg {String} fieldClass @hide
22853 * @cfg {String} focusClass @hide
22856 * @cfg {String} autoCreate @hide
22859 * @cfg {String} inputType @hide
22862 * @cfg {String} invalidClass @hide
22865 * @cfg {String} invalidText @hide
22868 * @cfg {String} msgFx @hide
22871 * @cfg {String} validateOnBlur @hide
22875 Roo.HtmlEditorCore.white = [
22876 'area', 'br', 'img', 'input', 'hr', 'wbr',
22878 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
22879 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
22880 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
22881 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
22882 'table', 'ul', 'xmp',
22884 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
22887 'dir', 'menu', 'ol', 'ul', 'dl',
22893 Roo.HtmlEditorCore.black = [
22894 // 'embed', 'object', // enable - backend responsiblity to clean thiese
22896 'base', 'basefont', 'bgsound', 'blink', 'body',
22897 'frame', 'frameset', 'head', 'html', 'ilayer',
22898 'iframe', 'layer', 'link', 'meta', 'object',
22899 'script', 'style' ,'title', 'xml' // clean later..
22901 Roo.HtmlEditorCore.clean = [
22902 'script', 'style', 'title', 'xml'
22904 Roo.HtmlEditorCore.remove = [
22909 Roo.HtmlEditorCore.ablack = [
22913 Roo.HtmlEditorCore.aclean = [
22914 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
22918 Roo.HtmlEditorCore.pwhite= [
22919 'http', 'https', 'mailto'
22922 // white listed style attributes.
22923 Roo.HtmlEditorCore.cwhite= [
22924 // 'text-align', /// default is to allow most things..
22930 // black listed style attributes.
22931 Roo.HtmlEditorCore.cblack= [
22932 // 'font-size' -- this can be set by the project
22936 Roo.HtmlEditorCore.swapCodes =[
22955 * @class Roo.bootstrap.HtmlEditor
22956 * @extends Roo.bootstrap.TextArea
22957 * Bootstrap HtmlEditor class
22960 * Create a new HtmlEditor
22961 * @param {Object} config The config object
22964 Roo.bootstrap.HtmlEditor = function(config){
22965 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
22966 if (!this.toolbars) {
22967 this.toolbars = [];
22970 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22973 * @event initialize
22974 * Fires when the editor is fully initialized (including the iframe)
22975 * @param {HtmlEditor} this
22980 * Fires when the editor is first receives the focus. Any insertion must wait
22981 * until after this event.
22982 * @param {HtmlEditor} this
22986 * @event beforesync
22987 * Fires before the textarea is updated with content from the editor iframe. Return false
22988 * to cancel the sync.
22989 * @param {HtmlEditor} this
22990 * @param {String} html
22994 * @event beforepush
22995 * Fires before the iframe editor is updated with content from the textarea. Return false
22996 * to cancel the push.
22997 * @param {HtmlEditor} this
22998 * @param {String} html
23003 * Fires when the textarea is updated with content from the editor iframe.
23004 * @param {HtmlEditor} this
23005 * @param {String} html
23010 * Fires when the iframe editor is updated with content from the textarea.
23011 * @param {HtmlEditor} this
23012 * @param {String} html
23016 * @event editmodechange
23017 * Fires when the editor switches edit modes
23018 * @param {HtmlEditor} this
23019 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23021 editmodechange: true,
23023 * @event editorevent
23024 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23025 * @param {HtmlEditor} this
23029 * @event firstfocus
23030 * Fires when on first focus - needed by toolbars..
23031 * @param {HtmlEditor} this
23036 * Auto save the htmlEditor value as a file into Events
23037 * @param {HtmlEditor} this
23041 * @event savedpreview
23042 * preview the saved version of htmlEditor
23043 * @param {HtmlEditor} this
23050 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
23054 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23059 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23064 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
23069 * @cfg {Number} height (in pixels)
23073 * @cfg {Number} width (in pixels)
23078 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23081 stylesheets: false,
23086 // private properties
23087 validationEvent : false,
23089 initialized : false,
23092 onFocus : Roo.emptyFn,
23094 hideMode:'offsets',
23096 tbContainer : false,
23100 toolbarContainer :function() {
23101 return this.wrap.select('.x-html-editor-tb',true).first();
23105 * Protected method that will not generally be called directly. It
23106 * is called when the editor creates its toolbar. Override this method if you need to
23107 * add custom toolbar buttons.
23108 * @param {HtmlEditor} editor
23110 createToolbar : function(){
23111 Roo.log('renewing');
23112 Roo.log("create toolbars");
23114 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23115 this.toolbars[0].render(this.toolbarContainer());
23119 // if (!editor.toolbars || !editor.toolbars.length) {
23120 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23123 // for (var i =0 ; i < editor.toolbars.length;i++) {
23124 // editor.toolbars[i] = Roo.factory(
23125 // typeof(editor.toolbars[i]) == 'string' ?
23126 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
23127 // Roo.bootstrap.HtmlEditor);
23128 // editor.toolbars[i].init(editor);
23134 onRender : function(ct, position)
23136 // Roo.log("Call onRender: " + this.xtype);
23138 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23140 this.wrap = this.inputEl().wrap({
23141 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23144 this.editorcore.onRender(ct, position);
23146 if (this.resizable) {
23147 this.resizeEl = new Roo.Resizable(this.wrap, {
23151 minHeight : this.height,
23152 height: this.height,
23153 handles : this.resizable,
23156 resize : function(r, w, h) {
23157 _t.onResize(w,h); // -something
23163 this.createToolbar(this);
23166 if(!this.width && this.resizable){
23167 this.setSize(this.wrap.getSize());
23169 if (this.resizeEl) {
23170 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23171 // should trigger onReize..
23177 onResize : function(w, h)
23179 Roo.log('resize: ' +w + ',' + h );
23180 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23184 if(this.inputEl() ){
23185 if(typeof w == 'number'){
23186 var aw = w - this.wrap.getFrameWidth('lr');
23187 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23190 if(typeof h == 'number'){
23191 var tbh = -11; // fixme it needs to tool bar size!
23192 for (var i =0; i < this.toolbars.length;i++) {
23193 // fixme - ask toolbars for heights?
23194 tbh += this.toolbars[i].el.getHeight();
23195 //if (this.toolbars[i].footer) {
23196 // tbh += this.toolbars[i].footer.el.getHeight();
23204 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23205 ah -= 5; // knock a few pixes off for look..
23206 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23210 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23211 this.editorcore.onResize(ew,eh);
23216 * Toggles the editor between standard and source edit mode.
23217 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23219 toggleSourceEdit : function(sourceEditMode)
23221 this.editorcore.toggleSourceEdit(sourceEditMode);
23223 if(this.editorcore.sourceEditMode){
23224 Roo.log('editor - showing textarea');
23227 // Roo.log(this.syncValue());
23229 this.inputEl().removeClass(['hide', 'x-hidden']);
23230 this.inputEl().dom.removeAttribute('tabIndex');
23231 this.inputEl().focus();
23233 Roo.log('editor - hiding textarea');
23235 // Roo.log(this.pushValue());
23238 this.inputEl().addClass(['hide', 'x-hidden']);
23239 this.inputEl().dom.setAttribute('tabIndex', -1);
23240 //this.deferFocus();
23243 if(this.resizable){
23244 this.setSize(this.wrap.getSize());
23247 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23250 // private (for BoxComponent)
23251 adjustSize : Roo.BoxComponent.prototype.adjustSize,
23253 // private (for BoxComponent)
23254 getResizeEl : function(){
23258 // private (for BoxComponent)
23259 getPositionEl : function(){
23264 initEvents : function(){
23265 this.originalValue = this.getValue();
23269 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23272 // markInvalid : Roo.emptyFn,
23274 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23277 // clearInvalid : Roo.emptyFn,
23279 setValue : function(v){
23280 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23281 this.editorcore.pushValue();
23286 deferFocus : function(){
23287 this.focus.defer(10, this);
23291 focus : function(){
23292 this.editorcore.focus();
23298 onDestroy : function(){
23304 for (var i =0; i < this.toolbars.length;i++) {
23305 // fixme - ask toolbars for heights?
23306 this.toolbars[i].onDestroy();
23309 this.wrap.dom.innerHTML = '';
23310 this.wrap.remove();
23315 onFirstFocus : function(){
23316 //Roo.log("onFirstFocus");
23317 this.editorcore.onFirstFocus();
23318 for (var i =0; i < this.toolbars.length;i++) {
23319 this.toolbars[i].onFirstFocus();
23325 syncValue : function()
23327 this.editorcore.syncValue();
23330 pushValue : function()
23332 this.editorcore.pushValue();
23336 // hide stuff that is not compatible
23350 * @event specialkey
23354 * @cfg {String} fieldClass @hide
23357 * @cfg {String} focusClass @hide
23360 * @cfg {String} autoCreate @hide
23363 * @cfg {String} inputType @hide
23366 * @cfg {String} invalidClass @hide
23369 * @cfg {String} invalidText @hide
23372 * @cfg {String} msgFx @hide
23375 * @cfg {String} validateOnBlur @hide
23384 Roo.namespace('Roo.bootstrap.htmleditor');
23386 * @class Roo.bootstrap.HtmlEditorToolbar1
23391 new Roo.bootstrap.HtmlEditor({
23394 new Roo.bootstrap.HtmlEditorToolbar1({
23395 disable : { fonts: 1 , format: 1, ..., ... , ...],
23401 * @cfg {Object} disable List of elements to disable..
23402 * @cfg {Array} btns List of additional buttons.
23406 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23409 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23412 Roo.apply(this, config);
23414 // default disabled, based on 'good practice'..
23415 this.disable = this.disable || {};
23416 Roo.applyIf(this.disable, {
23419 specialElements : true
23421 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23423 this.editor = config.editor;
23424 this.editorcore = config.editor.editorcore;
23426 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23428 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23429 // dont call parent... till later.
23431 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
23436 editorcore : false,
23441 "h1","h2","h3","h4","h5","h6",
23443 "abbr", "acronym", "address", "cite", "samp", "var",
23447 onRender : function(ct, position)
23449 // Roo.log("Call onRender: " + this.xtype);
23451 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23453 this.el.dom.style.marginBottom = '0';
23455 var editorcore = this.editorcore;
23456 var editor= this.editor;
23459 var btn = function(id,cmd , toggle, handler, html){
23461 var event = toggle ? 'toggle' : 'click';
23466 xns: Roo.bootstrap,
23469 enableToggle:toggle !== false,
23471 pressed : toggle ? false : null,
23474 a.listeners[toggle ? 'toggle' : 'click'] = function() {
23475 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
23481 // var cb_box = function...
23486 xns: Roo.bootstrap,
23487 glyphicon : 'font',
23491 xns: Roo.bootstrap,
23495 Roo.each(this.formats, function(f) {
23496 style.menu.items.push({
23498 xns: Roo.bootstrap,
23499 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23504 editorcore.insertTag(this.tagname);
23511 children.push(style);
23513 btn('bold',false,true);
23514 btn('italic',false,true);
23515 btn('align-left', 'justifyleft',true);
23516 btn('align-center', 'justifycenter',true);
23517 btn('align-right' , 'justifyright',true);
23518 btn('link', false, false, function(btn) {
23519 //Roo.log("create link?");
23520 var url = prompt(this.createLinkText, this.defaultLinkValue);
23521 if(url && url != 'http:/'+'/'){
23522 this.editorcore.relayCmd('createlink', url);
23525 btn('list','insertunorderedlist',true);
23526 btn('pencil', false,true, function(btn){
23528 this.toggleSourceEdit(btn.pressed);
23531 if (this.editor.btns.length > 0) {
23532 for (var i = 0; i<this.editor.btns.length; i++) {
23533 children.push(this.editor.btns[i]);
23541 xns: Roo.bootstrap,
23546 xns: Roo.bootstrap,
23551 cog.menu.items.push({
23553 xns: Roo.bootstrap,
23554 html : Clean styles,
23559 editorcore.insertTag(this.tagname);
23568 this.xtype = 'NavSimplebar';
23570 for(var i=0;i< children.length;i++) {
23572 this.buttons.add(this.addxtypeChild(children[i]));
23576 editor.on('editorevent', this.updateToolbar, this);
23578 onBtnClick : function(id)
23580 this.editorcore.relayCmd(id);
23581 this.editorcore.focus();
23585 * Protected method that will not generally be called directly. It triggers
23586 * a toolbar update by reading the markup state of the current selection in the editor.
23588 updateToolbar: function(){
23590 if(!this.editorcore.activated){
23591 this.editor.onFirstFocus(); // is this neeed?
23595 var btns = this.buttons;
23596 var doc = this.editorcore.doc;
23597 btns.get('bold').setActive(doc.queryCommandState('bold'));
23598 btns.get('italic').setActive(doc.queryCommandState('italic'));
23599 //btns.get('underline').setActive(doc.queryCommandState('underline'));
23601 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23602 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23603 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23605 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23606 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23609 var ans = this.editorcore.getAllAncestors();
23610 if (this.formatCombo) {
23613 var store = this.formatCombo.store;
23614 this.formatCombo.setValue("");
23615 for (var i =0; i < ans.length;i++) {
23616 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23618 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23626 // hides menus... - so this cant be on a menu...
23627 Roo.bootstrap.MenuMgr.hideAll();
23629 Roo.bootstrap.MenuMgr.hideAll();
23630 //this.editorsyncValue();
23632 onFirstFocus: function() {
23633 this.buttons.each(function(item){
23637 toggleSourceEdit : function(sourceEditMode){
23640 if(sourceEditMode){
23641 Roo.log("disabling buttons");
23642 this.buttons.each( function(item){
23643 if(item.cmd != 'pencil'){
23649 Roo.log("enabling buttons");
23650 if(this.editorcore.initialized){
23651 this.buttons.each( function(item){
23657 Roo.log("calling toggole on editor");
23658 // tell the editor that it's been pressed..
23659 this.editor.toggleSourceEdit(sourceEditMode);
23669 * @class Roo.bootstrap.Table.AbstractSelectionModel
23670 * @extends Roo.util.Observable
23671 * Abstract base class for grid SelectionModels. It provides the interface that should be
23672 * implemented by descendant classes. This class should not be directly instantiated.
23675 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23676 this.locked = false;
23677 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23681 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
23682 /** @ignore Called by the grid automatically. Do not call directly. */
23683 init : function(grid){
23689 * Locks the selections.
23692 this.locked = true;
23696 * Unlocks the selections.
23698 unlock : function(){
23699 this.locked = false;
23703 * Returns true if the selections are locked.
23704 * @return {Boolean}
23706 isLocked : function(){
23707 return this.locked;
23711 * @extends Roo.bootstrap.Table.AbstractSelectionModel
23712 * @class Roo.bootstrap.Table.RowSelectionModel
23713 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23714 * It supports multiple selections and keyboard selection/navigation.
23716 * @param {Object} config
23719 Roo.bootstrap.Table.RowSelectionModel = function(config){
23720 Roo.apply(this, config);
23721 this.selections = new Roo.util.MixedCollection(false, function(o){
23726 this.lastActive = false;
23730 * @event selectionchange
23731 * Fires when the selection changes
23732 * @param {SelectionModel} this
23734 "selectionchange" : true,
23736 * @event afterselectionchange
23737 * Fires after the selection changes (eg. by key press or clicking)
23738 * @param {SelectionModel} this
23740 "afterselectionchange" : true,
23742 * @event beforerowselect
23743 * Fires when a row is selected being selected, return false to cancel.
23744 * @param {SelectionModel} this
23745 * @param {Number} rowIndex The selected index
23746 * @param {Boolean} keepExisting False if other selections will be cleared
23748 "beforerowselect" : true,
23751 * Fires when a row is selected.
23752 * @param {SelectionModel} this
23753 * @param {Number} rowIndex The selected index
23754 * @param {Roo.data.Record} r The record
23756 "rowselect" : true,
23758 * @event rowdeselect
23759 * Fires when a row is deselected.
23760 * @param {SelectionModel} this
23761 * @param {Number} rowIndex The selected index
23763 "rowdeselect" : true
23765 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23766 this.locked = false;
23769 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
23771 * @cfg {Boolean} singleSelect
23772 * True to allow selection of only one row at a time (defaults to false)
23774 singleSelect : false,
23777 initEvents : function()
23780 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
23781 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
23782 //}else{ // allow click to work like normal
23783 // this.grid.on("rowclick", this.handleDragableRowClick, this);
23785 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
23786 this.grid.on("rowclick", this.handleMouseDown, this);
23788 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
23789 "up" : function(e){
23791 this.selectPrevious(e.shiftKey);
23792 }else if(this.last !== false && this.lastActive !== false){
23793 var last = this.last;
23794 this.selectRange(this.last, this.lastActive-1);
23795 this.grid.getView().focusRow(this.lastActive);
23796 if(last !== false){
23800 this.selectFirstRow();
23802 this.fireEvent("afterselectionchange", this);
23804 "down" : function(e){
23806 this.selectNext(e.shiftKey);
23807 }else if(this.last !== false && this.lastActive !== false){
23808 var last = this.last;
23809 this.selectRange(this.last, this.lastActive+1);
23810 this.grid.getView().focusRow(this.lastActive);
23811 if(last !== false){
23815 this.selectFirstRow();
23817 this.fireEvent("afterselectionchange", this);
23821 this.grid.store.on('load', function(){
23822 this.selections.clear();
23825 var view = this.grid.view;
23826 view.on("refresh", this.onRefresh, this);
23827 view.on("rowupdated", this.onRowUpdated, this);
23828 view.on("rowremoved", this.onRemove, this);
23833 onRefresh : function()
23835 var ds = this.grid.store, i, v = this.grid.view;
23836 var s = this.selections;
23837 s.each(function(r){
23838 if((i = ds.indexOfId(r.id)) != -1){
23847 onRemove : function(v, index, r){
23848 this.selections.remove(r);
23852 onRowUpdated : function(v, index, r){
23853 if(this.isSelected(r)){
23854 v.onRowSelect(index);
23860 * @param {Array} records The records to select
23861 * @param {Boolean} keepExisting (optional) True to keep existing selections
23863 selectRecords : function(records, keepExisting)
23866 this.clearSelections();
23868 var ds = this.grid.store;
23869 for(var i = 0, len = records.length; i < len; i++){
23870 this.selectRow(ds.indexOf(records[i]), true);
23875 * Gets the number of selected rows.
23878 getCount : function(){
23879 return this.selections.length;
23883 * Selects the first row in the grid.
23885 selectFirstRow : function(){
23890 * Select the last row.
23891 * @param {Boolean} keepExisting (optional) True to keep existing selections
23893 selectLastRow : function(keepExisting){
23894 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
23895 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
23899 * Selects the row immediately following the last selected row.
23900 * @param {Boolean} keepExisting (optional) True to keep existing selections
23902 selectNext : function(keepExisting)
23904 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
23905 this.selectRow(this.last+1, keepExisting);
23906 this.grid.getView().focusRow(this.last);
23911 * Selects the row that precedes the last selected row.
23912 * @param {Boolean} keepExisting (optional) True to keep existing selections
23914 selectPrevious : function(keepExisting){
23916 this.selectRow(this.last-1, keepExisting);
23917 this.grid.getView().focusRow(this.last);
23922 * Returns the selected records
23923 * @return {Array} Array of selected records
23925 getSelections : function(){
23926 return [].concat(this.selections.items);
23930 * Returns the first selected record.
23933 getSelected : function(){
23934 return this.selections.itemAt(0);
23939 * Clears all selections.
23941 clearSelections : function(fast)
23947 var ds = this.grid.store;
23948 var s = this.selections;
23949 s.each(function(r){
23950 this.deselectRow(ds.indexOfId(r.id));
23954 this.selections.clear();
23961 * Selects all rows.
23963 selectAll : function(){
23967 this.selections.clear();
23968 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
23969 this.selectRow(i, true);
23974 * Returns True if there is a selection.
23975 * @return {Boolean}
23977 hasSelection : function(){
23978 return this.selections.length > 0;
23982 * Returns True if the specified row is selected.
23983 * @param {Number/Record} record The record or index of the record to check
23984 * @return {Boolean}
23986 isSelected : function(index){
23987 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
23988 return (r && this.selections.key(r.id) ? true : false);
23992 * Returns True if the specified record id is selected.
23993 * @param {String} id The id of record to check
23994 * @return {Boolean}
23996 isIdSelected : function(id){
23997 return (this.selections.key(id) ? true : false);
24002 handleMouseDBClick : function(e, t){
24006 handleMouseDown : function(e, t)
24008 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24009 if(this.isLocked() || rowIndex < 0 ){
24012 if(e.shiftKey && this.last !== false){
24013 var last = this.last;
24014 this.selectRange(last, rowIndex, e.ctrlKey);
24015 this.last = last; // reset the last
24019 var isSelected = this.isSelected(rowIndex);
24020 //Roo.log("select row:" + rowIndex);
24022 this.deselectRow(rowIndex);
24024 this.selectRow(rowIndex, true);
24028 if(e.button !== 0 && isSelected){
24029 alert('rowIndex 2: ' + rowIndex);
24030 view.focusRow(rowIndex);
24031 }else if(e.ctrlKey && isSelected){
24032 this.deselectRow(rowIndex);
24033 }else if(!isSelected){
24034 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24035 view.focusRow(rowIndex);
24039 this.fireEvent("afterselectionchange", this);
24042 handleDragableRowClick : function(grid, rowIndex, e)
24044 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24045 this.selectRow(rowIndex, false);
24046 grid.view.focusRow(rowIndex);
24047 this.fireEvent("afterselectionchange", this);
24052 * Selects multiple rows.
24053 * @param {Array} rows Array of the indexes of the row to select
24054 * @param {Boolean} keepExisting (optional) True to keep existing selections
24056 selectRows : function(rows, keepExisting){
24058 this.clearSelections();
24060 for(var i = 0, len = rows.length; i < len; i++){
24061 this.selectRow(rows[i], true);
24066 * Selects a range of rows. All rows in between startRow and endRow are also selected.
24067 * @param {Number} startRow The index of the first row in the range
24068 * @param {Number} endRow The index of the last row in the range
24069 * @param {Boolean} keepExisting (optional) True to retain existing selections
24071 selectRange : function(startRow, endRow, keepExisting){
24076 this.clearSelections();
24078 if(startRow <= endRow){
24079 for(var i = startRow; i <= endRow; i++){
24080 this.selectRow(i, true);
24083 for(var i = startRow; i >= endRow; i--){
24084 this.selectRow(i, true);
24090 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24091 * @param {Number} startRow The index of the first row in the range
24092 * @param {Number} endRow The index of the last row in the range
24094 deselectRange : function(startRow, endRow, preventViewNotify){
24098 for(var i = startRow; i <= endRow; i++){
24099 this.deselectRow(i, preventViewNotify);
24105 * @param {Number} row The index of the row to select
24106 * @param {Boolean} keepExisting (optional) True to keep existing selections
24108 selectRow : function(index, keepExisting, preventViewNotify)
24110 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24113 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24114 if(!keepExisting || this.singleSelect){
24115 this.clearSelections();
24118 var r = this.grid.store.getAt(index);
24119 //console.log('selectRow - record id :' + r.id);
24121 this.selections.add(r);
24122 this.last = this.lastActive = index;
24123 if(!preventViewNotify){
24124 var proxy = new Roo.Element(
24125 this.grid.getRowDom(index)
24127 proxy.addClass('bg-info info');
24129 this.fireEvent("rowselect", this, index, r);
24130 this.fireEvent("selectionchange", this);
24136 * @param {Number} row The index of the row to deselect
24138 deselectRow : function(index, preventViewNotify)
24143 if(this.last == index){
24146 if(this.lastActive == index){
24147 this.lastActive = false;
24150 var r = this.grid.store.getAt(index);
24155 this.selections.remove(r);
24156 //.console.log('deselectRow - record id :' + r.id);
24157 if(!preventViewNotify){
24159 var proxy = new Roo.Element(
24160 this.grid.getRowDom(index)
24162 proxy.removeClass('bg-info info');
24164 this.fireEvent("rowdeselect", this, index);
24165 this.fireEvent("selectionchange", this);
24169 restoreLast : function(){
24171 this.last = this._last;
24176 acceptsNav : function(row, col, cm){
24177 return !cm.isHidden(col) && cm.isCellEditable(col, row);
24181 onEditorKey : function(field, e){
24182 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24187 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24189 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24191 }else if(k == e.ENTER && !e.ctrlKey){
24195 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24197 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24199 }else if(k == e.ESC){
24203 g.startEditing(newCell[0], newCell[1]);
24209 * Ext JS Library 1.1.1
24210 * Copyright(c) 2006-2007, Ext JS, LLC.
24212 * Originally Released Under LGPL - original licence link has changed is not relivant.
24215 * <script type="text/javascript">
24219 * @class Roo.bootstrap.PagingToolbar
24220 * @extends Roo.bootstrap.NavSimplebar
24221 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24223 * Create a new PagingToolbar
24224 * @param {Object} config The config object
24225 * @param {Roo.data.Store} store
24227 Roo.bootstrap.PagingToolbar = function(config)
24229 // old args format still supported... - xtype is prefered..
24230 // created from xtype...
24232 this.ds = config.dataSource;
24234 if (config.store && !this.ds) {
24235 this.store= Roo.factory(config.store, Roo.data);
24236 this.ds = this.store;
24237 this.ds.xmodule = this.xmodule || false;
24240 this.toolbarItems = [];
24241 if (config.items) {
24242 this.toolbarItems = config.items;
24245 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24250 this.bind(this.ds);
24253 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24257 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24259 * @cfg {Roo.data.Store} dataSource
24260 * The underlying data store providing the paged data
24263 * @cfg {String/HTMLElement/Element} container
24264 * container The id or element that will contain the toolbar
24267 * @cfg {Boolean} displayInfo
24268 * True to display the displayMsg (defaults to false)
24271 * @cfg {Number} pageSize
24272 * The number of records to display per page (defaults to 20)
24276 * @cfg {String} displayMsg
24277 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24279 displayMsg : 'Displaying {0} - {1} of {2}',
24281 * @cfg {String} emptyMsg
24282 * The message to display when no records are found (defaults to "No data to display")
24284 emptyMsg : 'No data to display',
24286 * Customizable piece of the default paging text (defaults to "Page")
24289 beforePageText : "Page",
24291 * Customizable piece of the default paging text (defaults to "of %0")
24294 afterPageText : "of {0}",
24296 * Customizable piece of the default paging text (defaults to "First Page")
24299 firstText : "First Page",
24301 * Customizable piece of the default paging text (defaults to "Previous Page")
24304 prevText : "Previous Page",
24306 * Customizable piece of the default paging text (defaults to "Next Page")
24309 nextText : "Next Page",
24311 * Customizable piece of the default paging text (defaults to "Last Page")
24314 lastText : "Last Page",
24316 * Customizable piece of the default paging text (defaults to "Refresh")
24319 refreshText : "Refresh",
24323 onRender : function(ct, position)
24325 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24326 this.navgroup.parentId = this.id;
24327 this.navgroup.onRender(this.el, null);
24328 // add the buttons to the navgroup
24330 if(this.displayInfo){
24331 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24332 this.displayEl = this.el.select('.x-paging-info', true).first();
24333 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24334 // this.displayEl = navel.el.select('span',true).first();
24340 Roo.each(_this.buttons, function(e){ // this might need to use render????
24341 Roo.factory(e).onRender(_this.el, null);
24345 Roo.each(_this.toolbarItems, function(e) {
24346 _this.navgroup.addItem(e);
24350 this.first = this.navgroup.addItem({
24351 tooltip: this.firstText,
24353 icon : 'fa fa-backward',
24355 preventDefault: true,
24356 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24359 this.prev = this.navgroup.addItem({
24360 tooltip: this.prevText,
24362 icon : 'fa fa-step-backward',
24364 preventDefault: true,
24365 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
24367 //this.addSeparator();
24370 var field = this.navgroup.addItem( {
24372 cls : 'x-paging-position',
24374 html : this.beforePageText +
24375 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24376 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
24379 this.field = field.el.select('input', true).first();
24380 this.field.on("keydown", this.onPagingKeydown, this);
24381 this.field.on("focus", function(){this.dom.select();});
24384 this.afterTextEl = field.el.select('.x-paging-after',true).first();
24385 //this.field.setHeight(18);
24386 //this.addSeparator();
24387 this.next = this.navgroup.addItem({
24388 tooltip: this.nextText,
24390 html : ' <i class="fa fa-step-forward">',
24392 preventDefault: true,
24393 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
24395 this.last = this.navgroup.addItem({
24396 tooltip: this.lastText,
24397 icon : 'fa fa-forward',
24400 preventDefault: true,
24401 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
24403 //this.addSeparator();
24404 this.loading = this.navgroup.addItem({
24405 tooltip: this.refreshText,
24406 icon: 'fa fa-refresh',
24407 preventDefault: true,
24408 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24414 updateInfo : function(){
24415 if(this.displayEl){
24416 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24417 var msg = count == 0 ?
24421 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
24423 this.displayEl.update(msg);
24428 onLoad : function(ds, r, o)
24430 this.cursor = o.params ? o.params.start : 0;
24431 var d = this.getPageData(),
24436 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24437 this.field.dom.value = ap;
24438 this.first.setDisabled(ap == 1);
24439 this.prev.setDisabled(ap == 1);
24440 this.next.setDisabled(ap == ps);
24441 this.last.setDisabled(ap == ps);
24442 this.loading.enable();
24447 getPageData : function(){
24448 var total = this.ds.getTotalCount();
24451 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24452 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24457 onLoadError : function(){
24458 this.loading.enable();
24462 onPagingKeydown : function(e){
24463 var k = e.getKey();
24464 var d = this.getPageData();
24466 var v = this.field.dom.value, pageNum;
24467 if(!v || isNaN(pageNum = parseInt(v, 10))){
24468 this.field.dom.value = d.activePage;
24471 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24472 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24475 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))
24477 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24478 this.field.dom.value = pageNum;
24479 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24482 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24484 var v = this.field.dom.value, pageNum;
24485 var increment = (e.shiftKey) ? 10 : 1;
24486 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24489 if(!v || isNaN(pageNum = parseInt(v, 10))) {
24490 this.field.dom.value = d.activePage;
24493 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24495 this.field.dom.value = parseInt(v, 10) + increment;
24496 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24497 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24504 beforeLoad : function(){
24506 this.loading.disable();
24511 onClick : function(which){
24520 ds.load({params:{start: 0, limit: this.pageSize}});
24523 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24526 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24529 var total = ds.getTotalCount();
24530 var extra = total % this.pageSize;
24531 var lastStart = extra ? (total - extra) : total-this.pageSize;
24532 ds.load({params:{start: lastStart, limit: this.pageSize}});
24535 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24541 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24542 * @param {Roo.data.Store} store The data store to unbind
24544 unbind : function(ds){
24545 ds.un("beforeload", this.beforeLoad, this);
24546 ds.un("load", this.onLoad, this);
24547 ds.un("loadexception", this.onLoadError, this);
24548 ds.un("remove", this.updateInfo, this);
24549 ds.un("add", this.updateInfo, this);
24550 this.ds = undefined;
24554 * Binds the paging toolbar to the specified {@link Roo.data.Store}
24555 * @param {Roo.data.Store} store The data store to bind
24557 bind : function(ds){
24558 ds.on("beforeload", this.beforeLoad, this);
24559 ds.on("load", this.onLoad, this);
24560 ds.on("loadexception", this.onLoadError, this);
24561 ds.on("remove", this.updateInfo, this);
24562 ds.on("add", this.updateInfo, this);
24573 * @class Roo.bootstrap.MessageBar
24574 * @extends Roo.bootstrap.Component
24575 * Bootstrap MessageBar class
24576 * @cfg {String} html contents of the MessageBar
24577 * @cfg {String} weight (info | success | warning | danger) default info
24578 * @cfg {String} beforeClass insert the bar before the given class
24579 * @cfg {Boolean} closable (true | false) default false
24580 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24583 * Create a new Element
24584 * @param {Object} config The config object
24587 Roo.bootstrap.MessageBar = function(config){
24588 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24591 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
24597 beforeClass: 'bootstrap-sticky-wrap',
24599 getAutoCreate : function(){
24603 cls: 'alert alert-dismissable alert-' + this.weight,
24608 html: this.html || ''
24614 cfg.cls += ' alert-messages-fixed';
24628 onRender : function(ct, position)
24630 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24633 var cfg = Roo.apply({}, this.getAutoCreate());
24637 cfg.cls += ' ' + this.cls;
24640 cfg.style = this.style;
24642 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24644 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24647 this.el.select('>button.close').on('click', this.hide, this);
24653 if (!this.rendered) {
24659 this.fireEvent('show', this);
24665 if (!this.rendered) {
24671 this.fireEvent('hide', this);
24674 update : function()
24676 // var e = this.el.dom.firstChild;
24678 // if(this.closable){
24679 // e = e.nextSibling;
24682 // e.data = this.html || '';
24684 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24700 * @class Roo.bootstrap.Graph
24701 * @extends Roo.bootstrap.Component
24702 * Bootstrap Graph class
24706 @cfg {String} graphtype bar | vbar | pie
24707 @cfg {number} g_x coodinator | centre x (pie)
24708 @cfg {number} g_y coodinator | centre y (pie)
24709 @cfg {number} g_r radius (pie)
24710 @cfg {number} g_height height of the chart (respected by all elements in the set)
24711 @cfg {number} g_width width of the chart (respected by all elements in the set)
24712 @cfg {Object} title The title of the chart
24715 -opts (object) options for the chart
24717 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24718 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24720 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.
24721 o stacked (boolean) whether or not to tread values as in a stacked bar chart
24723 o stretch (boolean)
24725 -opts (object) options for the pie
24728 o startAngle (number)
24729 o endAngle (number)
24733 * Create a new Input
24734 * @param {Object} config The config object
24737 Roo.bootstrap.Graph = function(config){
24738 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24744 * The img click event for the img.
24745 * @param {Roo.EventObject} e
24751 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
24762 //g_colors: this.colors,
24769 getAutoCreate : function(){
24780 onRender : function(ct,position){
24783 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
24785 if (typeof(Raphael) == 'undefined') {
24786 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
24790 this.raphael = Raphael(this.el.dom);
24792 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24793 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24794 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24795 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
24797 r.text(160, 10, "Single Series Chart").attr(txtattr);
24798 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
24799 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
24800 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
24802 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
24803 r.barchart(330, 10, 300, 220, data1);
24804 r.barchart(10, 250, 300, 220, data2, {stacked: true});
24805 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
24808 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24809 // r.barchart(30, 30, 560, 250, xdata, {
24810 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
24811 // axis : "0 0 1 1",
24812 // axisxlabels : xdata
24813 // //yvalues : cols,
24816 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24818 // this.load(null,xdata,{
24819 // axis : "0 0 1 1",
24820 // axisxlabels : xdata
24825 load : function(graphtype,xdata,opts)
24827 this.raphael.clear();
24829 graphtype = this.graphtype;
24834 var r = this.raphael,
24835 fin = function () {
24836 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
24838 fout = function () {
24839 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
24841 pfin = function() {
24842 this.sector.stop();
24843 this.sector.scale(1.1, 1.1, this.cx, this.cy);
24846 this.label[0].stop();
24847 this.label[0].attr({ r: 7.5 });
24848 this.label[1].attr({ "font-weight": 800 });
24851 pfout = function() {
24852 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
24855 this.label[0].animate({ r: 5 }, 500, "bounce");
24856 this.label[1].attr({ "font-weight": 400 });
24862 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24865 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24868 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
24869 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
24871 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
24878 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
24883 setTitle: function(o)
24888 initEvents: function() {
24891 this.el.on('click', this.onClick, this);
24895 onClick : function(e)
24897 Roo.log('img onclick');
24898 this.fireEvent('click', this, e);
24910 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24913 * @class Roo.bootstrap.dash.NumberBox
24914 * @extends Roo.bootstrap.Component
24915 * Bootstrap NumberBox class
24916 * @cfg {String} headline Box headline
24917 * @cfg {String} content Box content
24918 * @cfg {String} icon Box icon
24919 * @cfg {String} footer Footer text
24920 * @cfg {String} fhref Footer href
24923 * Create a new NumberBox
24924 * @param {Object} config The config object
24928 Roo.bootstrap.dash.NumberBox = function(config){
24929 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
24933 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
24942 getAutoCreate : function(){
24946 cls : 'small-box ',
24954 cls : 'roo-headline',
24955 html : this.headline
24959 cls : 'roo-content',
24960 html : this.content
24974 cls : 'ion ' + this.icon
24983 cls : 'small-box-footer',
24984 href : this.fhref || '#',
24988 cfg.cn.push(footer);
24995 onRender : function(ct,position){
24996 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25003 setHeadline: function (value)
25005 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25008 setFooter: function (value, href)
25010 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25013 this.el.select('a.small-box-footer',true).first().attr('href', href);
25018 setContent: function (value)
25020 this.el.select('.roo-content',true).first().dom.innerHTML = value;
25023 initEvents: function()
25037 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25040 * @class Roo.bootstrap.dash.TabBox
25041 * @extends Roo.bootstrap.Component
25042 * Bootstrap TabBox class
25043 * @cfg {String} title Title of the TabBox
25044 * @cfg {String} icon Icon of the TabBox
25045 * @cfg {Boolean} showtabs (true|false) show the tabs default true
25046 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25049 * Create a new TabBox
25050 * @param {Object} config The config object
25054 Roo.bootstrap.dash.TabBox = function(config){
25055 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25060 * When a pane is added
25061 * @param {Roo.bootstrap.dash.TabPane} pane
25065 * @event activatepane
25066 * When a pane is activated
25067 * @param {Roo.bootstrap.dash.TabPane} pane
25069 "activatepane" : true
25077 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
25082 tabScrollable : false,
25084 getChildContainer : function()
25086 return this.el.select('.tab-content', true).first();
25089 getAutoCreate : function(){
25093 cls: 'pull-left header',
25101 cls: 'fa ' + this.icon
25107 cls: 'nav nav-tabs pull-right',
25113 if(this.tabScrollable){
25120 cls: 'nav nav-tabs pull-right',
25131 cls: 'nav-tabs-custom',
25136 cls: 'tab-content no-padding',
25144 initEvents : function()
25146 //Roo.log('add add pane handler');
25147 this.on('addpane', this.onAddPane, this);
25150 * Updates the box title
25151 * @param {String} html to set the title to.
25153 setTitle : function(value)
25155 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25157 onAddPane : function(pane)
25159 this.panes.push(pane);
25160 //Roo.log('addpane');
25162 // tabs are rendere left to right..
25163 if(!this.showtabs){
25167 var ctr = this.el.select('.nav-tabs', true).first();
25170 var existing = ctr.select('.nav-tab',true);
25171 var qty = existing.getCount();;
25174 var tab = ctr.createChild({
25176 cls : 'nav-tab' + (qty ? '' : ' active'),
25184 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25187 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25189 pane.el.addClass('active');
25194 onTabClick : function(ev,un,ob,pane)
25196 //Roo.log('tab - prev default');
25197 ev.preventDefault();
25200 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25201 pane.tab.addClass('active');
25202 //Roo.log(pane.title);
25203 this.getChildContainer().select('.tab-pane',true).removeClass('active');
25204 // technically we should have a deactivate event.. but maybe add later.
25205 // and it should not de-activate the selected tab...
25206 this.fireEvent('activatepane', pane);
25207 pane.el.addClass('active');
25208 pane.fireEvent('activate');
25213 getActivePane : function()
25216 Roo.each(this.panes, function(p) {
25217 if(p.el.hasClass('active')){
25238 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25240 * @class Roo.bootstrap.TabPane
25241 * @extends Roo.bootstrap.Component
25242 * Bootstrap TabPane class
25243 * @cfg {Boolean} active (false | true) Default false
25244 * @cfg {String} title title of panel
25248 * Create a new TabPane
25249 * @param {Object} config The config object
25252 Roo.bootstrap.dash.TabPane = function(config){
25253 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25259 * When a pane is activated
25260 * @param {Roo.bootstrap.dash.TabPane} pane
25267 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
25272 // the tabBox that this is attached to.
25275 getAutoCreate : function()
25283 cfg.cls += ' active';
25288 initEvents : function()
25290 //Roo.log('trigger add pane handler');
25291 this.parent().fireEvent('addpane', this)
25295 * Updates the tab title
25296 * @param {String} html to set the title to.
25298 setTitle: function(str)
25304 this.tab.select('a', true).first().dom.innerHTML = str;
25321 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25324 * @class Roo.bootstrap.menu.Menu
25325 * @extends Roo.bootstrap.Component
25326 * Bootstrap Menu class - container for Menu
25327 * @cfg {String} html Text of the menu
25328 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25329 * @cfg {String} icon Font awesome icon
25330 * @cfg {String} pos Menu align to (top | bottom) default bottom
25334 * Create a new Menu
25335 * @param {Object} config The config object
25339 Roo.bootstrap.menu.Menu = function(config){
25340 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25344 * @event beforeshow
25345 * Fires before this menu is displayed
25346 * @param {Roo.bootstrap.menu.Menu} this
25350 * @event beforehide
25351 * Fires before this menu is hidden
25352 * @param {Roo.bootstrap.menu.Menu} this
25357 * Fires after this menu is displayed
25358 * @param {Roo.bootstrap.menu.Menu} this
25363 * Fires after this menu is hidden
25364 * @param {Roo.bootstrap.menu.Menu} this
25369 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25370 * @param {Roo.bootstrap.menu.Menu} this
25371 * @param {Roo.EventObject} e
25378 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
25382 weight : 'default',
25387 getChildContainer : function() {
25388 if(this.isSubMenu){
25392 return this.el.select('ul.dropdown-menu', true).first();
25395 getAutoCreate : function()
25400 cls : 'roo-menu-text',
25408 cls : 'fa ' + this.icon
25419 cls : 'dropdown-button btn btn-' + this.weight,
25424 cls : 'dropdown-toggle btn btn-' + this.weight,
25434 cls : 'dropdown-menu'
25440 if(this.pos == 'top'){
25441 cfg.cls += ' dropup';
25444 if(this.isSubMenu){
25447 cls : 'dropdown-menu'
25454 onRender : function(ct, position)
25456 this.isSubMenu = ct.hasClass('dropdown-submenu');
25458 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25461 initEvents : function()
25463 if(this.isSubMenu){
25467 this.hidden = true;
25469 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25470 this.triggerEl.on('click', this.onTriggerPress, this);
25472 this.buttonEl = this.el.select('button.dropdown-button', true).first();
25473 this.buttonEl.on('click', this.onClick, this);
25479 if(this.isSubMenu){
25483 return this.el.select('ul.dropdown-menu', true).first();
25486 onClick : function(e)
25488 this.fireEvent("click", this, e);
25491 onTriggerPress : function(e)
25493 if (this.isVisible()) {
25500 isVisible : function(){
25501 return !this.hidden;
25506 this.fireEvent("beforeshow", this);
25508 this.hidden = false;
25509 this.el.addClass('open');
25511 Roo.get(document).on("mouseup", this.onMouseUp, this);
25513 this.fireEvent("show", this);
25520 this.fireEvent("beforehide", this);
25522 this.hidden = true;
25523 this.el.removeClass('open');
25525 Roo.get(document).un("mouseup", this.onMouseUp);
25527 this.fireEvent("hide", this);
25530 onMouseUp : function()
25544 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25547 * @class Roo.bootstrap.menu.Item
25548 * @extends Roo.bootstrap.Component
25549 * Bootstrap MenuItem class
25550 * @cfg {Boolean} submenu (true | false) default false
25551 * @cfg {String} html text of the item
25552 * @cfg {String} href the link
25553 * @cfg {Boolean} disable (true | false) default false
25554 * @cfg {Boolean} preventDefault (true | false) default true
25555 * @cfg {String} icon Font awesome icon
25556 * @cfg {String} pos Submenu align to (left | right) default right
25560 * Create a new Item
25561 * @param {Object} config The config object
25565 Roo.bootstrap.menu.Item = function(config){
25566 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25570 * Fires when the mouse is hovering over this menu
25571 * @param {Roo.bootstrap.menu.Item} this
25572 * @param {Roo.EventObject} e
25577 * Fires when the mouse exits this menu
25578 * @param {Roo.bootstrap.menu.Item} this
25579 * @param {Roo.EventObject} e
25585 * The raw click event for the entire grid.
25586 * @param {Roo.EventObject} e
25592 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
25597 preventDefault: true,
25602 getAutoCreate : function()
25607 cls : 'roo-menu-item-text',
25615 cls : 'fa ' + this.icon
25624 href : this.href || '#',
25631 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25635 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25637 if(this.pos == 'left'){
25638 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25645 initEvents : function()
25647 this.el.on('mouseover', this.onMouseOver, this);
25648 this.el.on('mouseout', this.onMouseOut, this);
25650 this.el.select('a', true).first().on('click', this.onClick, this);
25654 onClick : function(e)
25656 if(this.preventDefault){
25657 e.preventDefault();
25660 this.fireEvent("click", this, e);
25663 onMouseOver : function(e)
25665 if(this.submenu && this.pos == 'left'){
25666 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25669 this.fireEvent("mouseover", this, e);
25672 onMouseOut : function(e)
25674 this.fireEvent("mouseout", this, e);
25686 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25689 * @class Roo.bootstrap.menu.Separator
25690 * @extends Roo.bootstrap.Component
25691 * Bootstrap Separator class
25694 * Create a new Separator
25695 * @param {Object} config The config object
25699 Roo.bootstrap.menu.Separator = function(config){
25700 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25703 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
25705 getAutoCreate : function(){
25726 * @class Roo.bootstrap.Tooltip
25727 * Bootstrap Tooltip class
25728 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25729 * to determine which dom element triggers the tooltip.
25731 * It needs to add support for additional attributes like tooltip-position
25734 * Create a new Toolti
25735 * @param {Object} config The config object
25738 Roo.bootstrap.Tooltip = function(config){
25739 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25741 this.alignment = Roo.bootstrap.Tooltip.alignment;
25743 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25744 this.alignment = config.alignment;
25749 Roo.apply(Roo.bootstrap.Tooltip, {
25751 * @function init initialize tooltip monitoring.
25755 currentTip : false,
25756 currentRegion : false,
25762 Roo.get(document).on('mouseover', this.enter ,this);
25763 Roo.get(document).on('mouseout', this.leave, this);
25766 this.currentTip = new Roo.bootstrap.Tooltip();
25769 enter : function(ev)
25771 var dom = ev.getTarget();
25773 //Roo.log(['enter',dom]);
25774 var el = Roo.fly(dom);
25775 if (this.currentEl) {
25777 //Roo.log(this.currentEl);
25778 //Roo.log(this.currentEl.contains(dom));
25779 if (this.currentEl == el) {
25782 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
25788 if (this.currentTip.el) {
25789 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
25793 if(!el || el.dom == document){
25799 // you can not look for children, as if el is the body.. then everythign is the child..
25800 if (!el.attr('tooltip')) { //
25801 if (!el.select("[tooltip]").elements.length) {
25804 // is the mouse over this child...?
25805 bindEl = el.select("[tooltip]").first();
25806 var xy = ev.getXY();
25807 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
25808 //Roo.log("not in region.");
25811 //Roo.log("child element over..");
25814 this.currentEl = bindEl;
25815 this.currentTip.bind(bindEl);
25816 this.currentRegion = Roo.lib.Region.getRegion(dom);
25817 this.currentTip.enter();
25820 leave : function(ev)
25822 var dom = ev.getTarget();
25823 //Roo.log(['leave',dom]);
25824 if (!this.currentEl) {
25829 if (dom != this.currentEl.dom) {
25832 var xy = ev.getXY();
25833 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
25836 // only activate leave if mouse cursor is outside... bounding box..
25841 if (this.currentTip) {
25842 this.currentTip.leave();
25844 //Roo.log('clear currentEl');
25845 this.currentEl = false;
25850 'left' : ['r-l', [-2,0], 'right'],
25851 'right' : ['l-r', [2,0], 'left'],
25852 'bottom' : ['t-b', [0,2], 'top'],
25853 'top' : [ 'b-t', [0,-2], 'bottom']
25859 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
25864 delay : null, // can be { show : 300 , hide: 500}
25868 hoverState : null, //???
25870 placement : 'bottom',
25874 getAutoCreate : function(){
25881 cls : 'tooltip-arrow'
25884 cls : 'tooltip-inner'
25891 bind : function(el)
25897 enter : function () {
25899 if (this.timeout != null) {
25900 clearTimeout(this.timeout);
25903 this.hoverState = 'in';
25904 //Roo.log("enter - show");
25905 if (!this.delay || !this.delay.show) {
25910 this.timeout = setTimeout(function () {
25911 if (_t.hoverState == 'in') {
25914 }, this.delay.show);
25918 clearTimeout(this.timeout);
25920 this.hoverState = 'out';
25921 if (!this.delay || !this.delay.hide) {
25927 this.timeout = setTimeout(function () {
25928 //Roo.log("leave - timeout");
25930 if (_t.hoverState == 'out') {
25932 Roo.bootstrap.Tooltip.currentEl = false;
25937 show : function (msg)
25940 this.render(document.body);
25943 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
25945 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
25947 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
25949 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
25951 var placement = typeof this.placement == 'function' ?
25952 this.placement.call(this, this.el, on_el) :
25955 var autoToken = /\s?auto?\s?/i;
25956 var autoPlace = autoToken.test(placement);
25958 placement = placement.replace(autoToken, '') || 'top';
25962 //this.el.setXY([0,0]);
25964 //this.el.dom.style.display='block';
25966 //this.el.appendTo(on_el);
25968 var p = this.getPosition();
25969 var box = this.el.getBox();
25975 var align = this.alignment[placement];
25977 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
25979 if(placement == 'top' || placement == 'bottom'){
25981 placement = 'right';
25984 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
25985 placement = 'left';
25988 var scroll = Roo.select('body', true).first().getScroll();
25990 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
25996 this.el.alignTo(this.bindEl, align[0],align[1]);
25997 //var arrow = this.el.select('.arrow',true).first();
25998 //arrow.set(align[2],
26000 this.el.addClass(placement);
26002 this.el.addClass('in fade');
26004 this.hoverState = null;
26006 if (this.el.hasClass('fade')) {
26017 //this.el.setXY([0,0]);
26018 this.el.removeClass('in');
26034 * @class Roo.bootstrap.LocationPicker
26035 * @extends Roo.bootstrap.Component
26036 * Bootstrap LocationPicker class
26037 * @cfg {Number} latitude Position when init default 0
26038 * @cfg {Number} longitude Position when init default 0
26039 * @cfg {Number} zoom default 15
26040 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26041 * @cfg {Boolean} mapTypeControl default false
26042 * @cfg {Boolean} disableDoubleClickZoom default false
26043 * @cfg {Boolean} scrollwheel default true
26044 * @cfg {Boolean} streetViewControl default false
26045 * @cfg {Number} radius default 0
26046 * @cfg {String} locationName
26047 * @cfg {Boolean} draggable default true
26048 * @cfg {Boolean} enableAutocomplete default false
26049 * @cfg {Boolean} enableReverseGeocode default true
26050 * @cfg {String} markerTitle
26053 * Create a new LocationPicker
26054 * @param {Object} config The config object
26058 Roo.bootstrap.LocationPicker = function(config){
26060 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26065 * Fires when the picker initialized.
26066 * @param {Roo.bootstrap.LocationPicker} this
26067 * @param {Google Location} location
26071 * @event positionchanged
26072 * Fires when the picker position changed.
26073 * @param {Roo.bootstrap.LocationPicker} this
26074 * @param {Google Location} location
26076 positionchanged : true,
26079 * Fires when the map resize.
26080 * @param {Roo.bootstrap.LocationPicker} this
26085 * Fires when the map show.
26086 * @param {Roo.bootstrap.LocationPicker} this
26091 * Fires when the map hide.
26092 * @param {Roo.bootstrap.LocationPicker} this
26097 * Fires when click the map.
26098 * @param {Roo.bootstrap.LocationPicker} this
26099 * @param {Map event} e
26103 * @event mapRightClick
26104 * Fires when right click the map.
26105 * @param {Roo.bootstrap.LocationPicker} this
26106 * @param {Map event} e
26108 mapRightClick : true,
26110 * @event markerClick
26111 * Fires when click the marker.
26112 * @param {Roo.bootstrap.LocationPicker} this
26113 * @param {Map event} e
26115 markerClick : true,
26117 * @event markerRightClick
26118 * Fires when right click the marker.
26119 * @param {Roo.bootstrap.LocationPicker} this
26120 * @param {Map event} e
26122 markerRightClick : true,
26124 * @event OverlayViewDraw
26125 * Fires when OverlayView Draw
26126 * @param {Roo.bootstrap.LocationPicker} this
26128 OverlayViewDraw : true,
26130 * @event OverlayViewOnAdd
26131 * Fires when OverlayView Draw
26132 * @param {Roo.bootstrap.LocationPicker} this
26134 OverlayViewOnAdd : true,
26136 * @event OverlayViewOnRemove
26137 * Fires when OverlayView Draw
26138 * @param {Roo.bootstrap.LocationPicker} this
26140 OverlayViewOnRemove : true,
26142 * @event OverlayViewShow
26143 * Fires when OverlayView Draw
26144 * @param {Roo.bootstrap.LocationPicker} this
26145 * @param {Pixel} cpx
26147 OverlayViewShow : true,
26149 * @event OverlayViewHide
26150 * Fires when OverlayView Draw
26151 * @param {Roo.bootstrap.LocationPicker} this
26153 OverlayViewHide : true,
26155 * @event loadexception
26156 * Fires when load google lib failed.
26157 * @param {Roo.bootstrap.LocationPicker} this
26159 loadexception : true
26164 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
26166 gMapContext: false,
26172 mapTypeControl: false,
26173 disableDoubleClickZoom: false,
26175 streetViewControl: false,
26179 enableAutocomplete: false,
26180 enableReverseGeocode: true,
26183 getAutoCreate: function()
26188 cls: 'roo-location-picker'
26194 initEvents: function(ct, position)
26196 if(!this.el.getWidth() || this.isApplied()){
26200 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26205 initial: function()
26207 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26208 this.fireEvent('loadexception', this);
26212 if(!this.mapTypeId){
26213 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26216 this.gMapContext = this.GMapContext();
26218 this.initOverlayView();
26220 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26224 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26225 _this.setPosition(_this.gMapContext.marker.position);
26228 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26229 _this.fireEvent('mapClick', this, event);
26233 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26234 _this.fireEvent('mapRightClick', this, event);
26238 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26239 _this.fireEvent('markerClick', this, event);
26243 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26244 _this.fireEvent('markerRightClick', this, event);
26248 this.setPosition(this.gMapContext.location);
26250 this.fireEvent('initial', this, this.gMapContext.location);
26253 initOverlayView: function()
26257 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26261 _this.fireEvent('OverlayViewDraw', _this);
26266 _this.fireEvent('OverlayViewOnAdd', _this);
26269 onRemove: function()
26271 _this.fireEvent('OverlayViewOnRemove', _this);
26274 show: function(cpx)
26276 _this.fireEvent('OverlayViewShow', _this, cpx);
26281 _this.fireEvent('OverlayViewHide', _this);
26287 fromLatLngToContainerPixel: function(event)
26289 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26292 isApplied: function()
26294 return this.getGmapContext() == false ? false : true;
26297 getGmapContext: function()
26299 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26302 GMapContext: function()
26304 var position = new google.maps.LatLng(this.latitude, this.longitude);
26306 var _map = new google.maps.Map(this.el.dom, {
26309 mapTypeId: this.mapTypeId,
26310 mapTypeControl: this.mapTypeControl,
26311 disableDoubleClickZoom: this.disableDoubleClickZoom,
26312 scrollwheel: this.scrollwheel,
26313 streetViewControl: this.streetViewControl,
26314 locationName: this.locationName,
26315 draggable: this.draggable,
26316 enableAutocomplete: this.enableAutocomplete,
26317 enableReverseGeocode: this.enableReverseGeocode
26320 var _marker = new google.maps.Marker({
26321 position: position,
26323 title: this.markerTitle,
26324 draggable: this.draggable
26331 location: position,
26332 radius: this.radius,
26333 locationName: this.locationName,
26334 addressComponents: {
26335 formatted_address: null,
26336 addressLine1: null,
26337 addressLine2: null,
26339 streetNumber: null,
26343 stateOrProvince: null
26346 domContainer: this.el.dom,
26347 geodecoder: new google.maps.Geocoder()
26351 drawCircle: function(center, radius, options)
26353 if (this.gMapContext.circle != null) {
26354 this.gMapContext.circle.setMap(null);
26358 options = Roo.apply({}, options, {
26359 strokeColor: "#0000FF",
26360 strokeOpacity: .35,
26362 fillColor: "#0000FF",
26366 options.map = this.gMapContext.map;
26367 options.radius = radius;
26368 options.center = center;
26369 this.gMapContext.circle = new google.maps.Circle(options);
26370 return this.gMapContext.circle;
26376 setPosition: function(location)
26378 this.gMapContext.location = location;
26379 this.gMapContext.marker.setPosition(location);
26380 this.gMapContext.map.panTo(location);
26381 this.drawCircle(location, this.gMapContext.radius, {});
26385 if (this.gMapContext.settings.enableReverseGeocode) {
26386 this.gMapContext.geodecoder.geocode({
26387 latLng: this.gMapContext.location
26388 }, function(results, status) {
26390 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26391 _this.gMapContext.locationName = results[0].formatted_address;
26392 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26394 _this.fireEvent('positionchanged', this, location);
26401 this.fireEvent('positionchanged', this, location);
26406 google.maps.event.trigger(this.gMapContext.map, "resize");
26408 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26410 this.fireEvent('resize', this);
26413 setPositionByLatLng: function(latitude, longitude)
26415 this.setPosition(new google.maps.LatLng(latitude, longitude));
26418 getCurrentPosition: function()
26421 latitude: this.gMapContext.location.lat(),
26422 longitude: this.gMapContext.location.lng()
26426 getAddressName: function()
26428 return this.gMapContext.locationName;
26431 getAddressComponents: function()
26433 return this.gMapContext.addressComponents;
26436 address_component_from_google_geocode: function(address_components)
26440 for (var i = 0; i < address_components.length; i++) {
26441 var component = address_components[i];
26442 if (component.types.indexOf("postal_code") >= 0) {
26443 result.postalCode = component.short_name;
26444 } else if (component.types.indexOf("street_number") >= 0) {
26445 result.streetNumber = component.short_name;
26446 } else if (component.types.indexOf("route") >= 0) {
26447 result.streetName = component.short_name;
26448 } else if (component.types.indexOf("neighborhood") >= 0) {
26449 result.city = component.short_name;
26450 } else if (component.types.indexOf("locality") >= 0) {
26451 result.city = component.short_name;
26452 } else if (component.types.indexOf("sublocality") >= 0) {
26453 result.district = component.short_name;
26454 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26455 result.stateOrProvince = component.short_name;
26456 } else if (component.types.indexOf("country") >= 0) {
26457 result.country = component.short_name;
26461 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26462 result.addressLine2 = "";
26466 setZoomLevel: function(zoom)
26468 this.gMapContext.map.setZoom(zoom);
26481 this.fireEvent('show', this);
26492 this.fireEvent('hide', this);
26497 Roo.apply(Roo.bootstrap.LocationPicker, {
26499 OverlayView : function(map, options)
26501 options = options || {};
26515 * @class Roo.bootstrap.Alert
26516 * @extends Roo.bootstrap.Component
26517 * Bootstrap Alert class
26518 * @cfg {String} title The title of alert
26519 * @cfg {String} html The content of alert
26520 * @cfg {String} weight ( success | info | warning | danger )
26521 * @cfg {String} faicon font-awesomeicon
26524 * Create a new alert
26525 * @param {Object} config The config object
26529 Roo.bootstrap.Alert = function(config){
26530 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26534 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
26541 getAutoCreate : function()
26550 cls : 'roo-alert-icon'
26555 cls : 'roo-alert-title',
26560 cls : 'roo-alert-text',
26567 cfg.cn[0].cls += ' fa ' + this.faicon;
26571 cfg.cls += ' alert-' + this.weight;
26577 initEvents: function()
26579 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26582 setTitle : function(str)
26584 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26587 setText : function(str)
26589 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26592 setWeight : function(weight)
26595 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26598 this.weight = weight;
26600 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26603 setIcon : function(icon)
26606 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26609 this.faicon = icon;
26611 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26632 * @class Roo.bootstrap.UploadCropbox
26633 * @extends Roo.bootstrap.Component
26634 * Bootstrap UploadCropbox class
26635 * @cfg {String} emptyText show when image has been loaded
26636 * @cfg {String} rotateNotify show when image too small to rotate
26637 * @cfg {Number} errorTimeout default 3000
26638 * @cfg {Number} minWidth default 300
26639 * @cfg {Number} minHeight default 300
26640 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26641 * @cfg {Boolean} isDocument (true|false) default false
26642 * @cfg {String} url action url
26643 * @cfg {String} paramName default 'imageUpload'
26644 * @cfg {String} method default POST
26645 * @cfg {Boolean} loadMask (true|false) default true
26646 * @cfg {Boolean} loadingText default 'Loading...'
26649 * Create a new UploadCropbox
26650 * @param {Object} config The config object
26653 Roo.bootstrap.UploadCropbox = function(config){
26654 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26658 * @event beforeselectfile
26659 * Fire before select file
26660 * @param {Roo.bootstrap.UploadCropbox} this
26662 "beforeselectfile" : true,
26665 * Fire after initEvent
26666 * @param {Roo.bootstrap.UploadCropbox} this
26671 * Fire after initEvent
26672 * @param {Roo.bootstrap.UploadCropbox} this
26673 * @param {String} data
26678 * Fire when preparing the file data
26679 * @param {Roo.bootstrap.UploadCropbox} this
26680 * @param {Object} file
26685 * Fire when get exception
26686 * @param {Roo.bootstrap.UploadCropbox} this
26687 * @param {XMLHttpRequest} xhr
26689 "exception" : true,
26691 * @event beforeloadcanvas
26692 * Fire before load the canvas
26693 * @param {Roo.bootstrap.UploadCropbox} this
26694 * @param {String} src
26696 "beforeloadcanvas" : true,
26699 * Fire when trash image
26700 * @param {Roo.bootstrap.UploadCropbox} this
26705 * Fire when download the image
26706 * @param {Roo.bootstrap.UploadCropbox} this
26710 * @event footerbuttonclick
26711 * Fire when footerbuttonclick
26712 * @param {Roo.bootstrap.UploadCropbox} this
26713 * @param {String} type
26715 "footerbuttonclick" : true,
26719 * @param {Roo.bootstrap.UploadCropbox} this
26724 * Fire when rotate the image
26725 * @param {Roo.bootstrap.UploadCropbox} this
26726 * @param {String} pos
26731 * Fire when inspect the file
26732 * @param {Roo.bootstrap.UploadCropbox} this
26733 * @param {Object} file
26738 * Fire when xhr upload the file
26739 * @param {Roo.bootstrap.UploadCropbox} this
26740 * @param {Object} data
26745 * Fire when arrange the file data
26746 * @param {Roo.bootstrap.UploadCropbox} this
26747 * @param {Object} formData
26752 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26755 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
26757 emptyText : 'Click to upload image',
26758 rotateNotify : 'Image is too small to rotate',
26759 errorTimeout : 3000,
26773 cropType : 'image/jpeg',
26775 canvasLoaded : false,
26776 isDocument : false,
26778 paramName : 'imageUpload',
26780 loadingText : 'Loading...',
26783 getAutoCreate : function()
26787 cls : 'roo-upload-cropbox',
26791 cls : 'roo-upload-cropbox-selector',
26796 cls : 'roo-upload-cropbox-body',
26797 style : 'cursor:pointer',
26801 cls : 'roo-upload-cropbox-preview'
26805 cls : 'roo-upload-cropbox-thumb'
26809 cls : 'roo-upload-cropbox-empty-notify',
26810 html : this.emptyText
26814 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
26815 html : this.rotateNotify
26821 cls : 'roo-upload-cropbox-footer',
26824 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
26834 onRender : function(ct, position)
26836 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
26838 if (this.buttons.length) {
26840 Roo.each(this.buttons, function(bb) {
26842 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
26844 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
26850 this.maskEl = this.el;
26854 initEvents : function()
26856 this.urlAPI = (window.createObjectURL && window) ||
26857 (window.URL && URL.revokeObjectURL && URL) ||
26858 (window.webkitURL && webkitURL);
26860 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
26861 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26863 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
26864 this.selectorEl.hide();
26866 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
26867 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26869 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
26870 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26871 this.thumbEl.hide();
26873 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
26874 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26876 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
26877 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26878 this.errorEl.hide();
26880 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
26881 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26882 this.footerEl.hide();
26884 this.setThumbBoxSize();
26890 this.fireEvent('initial', this);
26897 window.addEventListener("resize", function() { _this.resize(); } );
26899 this.bodyEl.on('click', this.beforeSelectFile, this);
26902 this.bodyEl.on('touchstart', this.onTouchStart, this);
26903 this.bodyEl.on('touchmove', this.onTouchMove, this);
26904 this.bodyEl.on('touchend', this.onTouchEnd, this);
26908 this.bodyEl.on('mousedown', this.onMouseDown, this);
26909 this.bodyEl.on('mousemove', this.onMouseMove, this);
26910 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
26911 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
26912 Roo.get(document).on('mouseup', this.onMouseUp, this);
26915 this.selectorEl.on('change', this.onFileSelected, this);
26921 this.baseScale = 1;
26923 this.baseRotate = 1;
26924 this.dragable = false;
26925 this.pinching = false;
26928 this.cropData = false;
26929 this.notifyEl.dom.innerHTML = this.emptyText;
26931 this.selectorEl.dom.value = '';
26935 resize : function()
26937 if(this.fireEvent('resize', this) != false){
26938 this.setThumbBoxPosition();
26939 this.setCanvasPosition();
26943 onFooterButtonClick : function(e, el, o, type)
26946 case 'rotate-left' :
26947 this.onRotateLeft(e);
26949 case 'rotate-right' :
26950 this.onRotateRight(e);
26953 this.beforeSelectFile(e);
26968 this.fireEvent('footerbuttonclick', this, type);
26971 beforeSelectFile : function(e)
26973 e.preventDefault();
26975 if(this.fireEvent('beforeselectfile', this) != false){
26976 this.selectorEl.dom.click();
26980 onFileSelected : function(e)
26982 e.preventDefault();
26984 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26988 var file = this.selectorEl.dom.files[0];
26990 if(this.fireEvent('inspect', this, file) != false){
26991 this.prepare(file);
26996 trash : function(e)
26998 this.fireEvent('trash', this);
27001 download : function(e)
27003 this.fireEvent('download', this);
27006 loadCanvas : function(src)
27008 if(this.fireEvent('beforeloadcanvas', this, src) != false){
27012 this.imageEl = document.createElement('img');
27016 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27018 this.imageEl.src = src;
27022 onLoadCanvas : function()
27024 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27025 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27027 this.bodyEl.un('click', this.beforeSelectFile, this);
27029 this.notifyEl.hide();
27030 this.thumbEl.show();
27031 this.footerEl.show();
27033 this.baseRotateLevel();
27035 if(this.isDocument){
27036 this.setThumbBoxSize();
27039 this.setThumbBoxPosition();
27041 this.baseScaleLevel();
27047 this.canvasLoaded = true;
27050 this.maskEl.unmask();
27055 setCanvasPosition : function()
27057 if(!this.canvasEl){
27061 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27062 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27064 this.previewEl.setLeft(pw);
27065 this.previewEl.setTop(ph);
27069 onMouseDown : function(e)
27073 this.dragable = true;
27074 this.pinching = false;
27076 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27077 this.dragable = false;
27081 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27082 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27086 onMouseMove : function(e)
27090 if(!this.canvasLoaded){
27094 if (!this.dragable){
27098 var minX = Math.ceil(this.thumbEl.getLeft(true));
27099 var minY = Math.ceil(this.thumbEl.getTop(true));
27101 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27102 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27104 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27105 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27107 x = x - this.mouseX;
27108 y = y - this.mouseY;
27110 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27111 var bgY = Math.ceil(y + this.previewEl.getTop(true));
27113 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27114 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27116 this.previewEl.setLeft(bgX);
27117 this.previewEl.setTop(bgY);
27119 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27120 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27123 onMouseUp : function(e)
27127 this.dragable = false;
27130 onMouseWheel : function(e)
27134 this.startScale = this.scale;
27136 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27138 if(!this.zoomable()){
27139 this.scale = this.startScale;
27148 zoomable : function()
27150 var minScale = this.thumbEl.getWidth() / this.minWidth;
27152 if(this.minWidth < this.minHeight){
27153 minScale = this.thumbEl.getHeight() / this.minHeight;
27156 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27157 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27161 (this.rotate == 0 || this.rotate == 180) &&
27163 width > this.imageEl.OriginWidth ||
27164 height > this.imageEl.OriginHeight ||
27165 (width < this.minWidth && height < this.minHeight)
27173 (this.rotate == 90 || this.rotate == 270) &&
27175 width > this.imageEl.OriginWidth ||
27176 height > this.imageEl.OriginHeight ||
27177 (width < this.minHeight && height < this.minWidth)
27184 !this.isDocument &&
27185 (this.rotate == 0 || this.rotate == 180) &&
27187 width < this.minWidth ||
27188 width > this.imageEl.OriginWidth ||
27189 height < this.minHeight ||
27190 height > this.imageEl.OriginHeight
27197 !this.isDocument &&
27198 (this.rotate == 90 || this.rotate == 270) &&
27200 width < this.minHeight ||
27201 width > this.imageEl.OriginWidth ||
27202 height < this.minWidth ||
27203 height > this.imageEl.OriginHeight
27213 onRotateLeft : function(e)
27215 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27217 var minScale = this.thumbEl.getWidth() / this.minWidth;
27219 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27220 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27222 this.startScale = this.scale;
27224 while (this.getScaleLevel() < minScale){
27226 this.scale = this.scale + 1;
27228 if(!this.zoomable()){
27233 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27234 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27239 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27246 this.scale = this.startScale;
27248 this.onRotateFail();
27253 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27255 if(this.isDocument){
27256 this.setThumbBoxSize();
27257 this.setThumbBoxPosition();
27258 this.setCanvasPosition();
27263 this.fireEvent('rotate', this, 'left');
27267 onRotateRight : function(e)
27269 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27271 var minScale = this.thumbEl.getWidth() / this.minWidth;
27273 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27274 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27276 this.startScale = this.scale;
27278 while (this.getScaleLevel() < minScale){
27280 this.scale = this.scale + 1;
27282 if(!this.zoomable()){
27287 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27288 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27293 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27300 this.scale = this.startScale;
27302 this.onRotateFail();
27307 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27309 if(this.isDocument){
27310 this.setThumbBoxSize();
27311 this.setThumbBoxPosition();
27312 this.setCanvasPosition();
27317 this.fireEvent('rotate', this, 'right');
27320 onRotateFail : function()
27322 this.errorEl.show(true);
27326 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27331 this.previewEl.dom.innerHTML = '';
27333 var canvasEl = document.createElement("canvas");
27335 var contextEl = canvasEl.getContext("2d");
27337 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27338 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27339 var center = this.imageEl.OriginWidth / 2;
27341 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27342 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27343 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27344 center = this.imageEl.OriginHeight / 2;
27347 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27349 contextEl.translate(center, center);
27350 contextEl.rotate(this.rotate * Math.PI / 180);
27352 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27354 this.canvasEl = document.createElement("canvas");
27356 this.contextEl = this.canvasEl.getContext("2d");
27358 switch (this.rotate) {
27361 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27362 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27364 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27369 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27370 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27372 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27373 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);
27377 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27382 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27383 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27385 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27386 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);
27390 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);
27395 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27396 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27398 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27399 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27403 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);
27410 this.previewEl.appendChild(this.canvasEl);
27412 this.setCanvasPosition();
27417 if(!this.canvasLoaded){
27421 var imageCanvas = document.createElement("canvas");
27423 var imageContext = imageCanvas.getContext("2d");
27425 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27426 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27428 var center = imageCanvas.width / 2;
27430 imageContext.translate(center, center);
27432 imageContext.rotate(this.rotate * Math.PI / 180);
27434 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27436 var canvas = document.createElement("canvas");
27438 var context = canvas.getContext("2d");
27440 canvas.width = this.minWidth;
27441 canvas.height = this.minHeight;
27443 switch (this.rotate) {
27446 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27447 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27449 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27450 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27452 var targetWidth = this.minWidth - 2 * x;
27453 var targetHeight = this.minHeight - 2 * y;
27457 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27458 scale = targetWidth / width;
27461 if(x > 0 && y == 0){
27462 scale = targetHeight / height;
27465 if(x > 0 && y > 0){
27466 scale = targetWidth / width;
27468 if(width < height){
27469 scale = targetHeight / height;
27473 context.scale(scale, scale);
27475 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27476 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27478 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27479 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27481 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27486 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27487 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27489 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27490 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27492 var targetWidth = this.minWidth - 2 * x;
27493 var targetHeight = this.minHeight - 2 * y;
27497 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27498 scale = targetWidth / width;
27501 if(x > 0 && y == 0){
27502 scale = targetHeight / height;
27505 if(x > 0 && y > 0){
27506 scale = targetWidth / width;
27508 if(width < height){
27509 scale = targetHeight / height;
27513 context.scale(scale, scale);
27515 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27516 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27518 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27519 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27521 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27523 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27528 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27529 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27531 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27532 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27534 var targetWidth = this.minWidth - 2 * x;
27535 var targetHeight = this.minHeight - 2 * y;
27539 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27540 scale = targetWidth / width;
27543 if(x > 0 && y == 0){
27544 scale = targetHeight / height;
27547 if(x > 0 && y > 0){
27548 scale = targetWidth / width;
27550 if(width < height){
27551 scale = targetHeight / height;
27555 context.scale(scale, scale);
27557 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27558 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27560 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27561 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27563 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27564 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27566 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27571 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27572 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27574 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27575 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27577 var targetWidth = this.minWidth - 2 * x;
27578 var targetHeight = this.minHeight - 2 * y;
27582 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27583 scale = targetWidth / width;
27586 if(x > 0 && y == 0){
27587 scale = targetHeight / height;
27590 if(x > 0 && y > 0){
27591 scale = targetWidth / width;
27593 if(width < height){
27594 scale = targetHeight / height;
27598 context.scale(scale, scale);
27600 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27601 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27603 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27604 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27606 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27608 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27615 this.cropData = canvas.toDataURL(this.cropType);
27617 if(this.fireEvent('crop', this, this.cropData) !== false){
27618 this.process(this.file, this.cropData);
27625 setThumbBoxSize : function()
27629 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27630 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27631 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27633 this.minWidth = width;
27634 this.minHeight = height;
27636 if(this.rotate == 90 || this.rotate == 270){
27637 this.minWidth = height;
27638 this.minHeight = width;
27643 width = Math.ceil(this.minWidth * height / this.minHeight);
27645 if(this.minWidth > this.minHeight){
27647 height = Math.ceil(this.minHeight * width / this.minWidth);
27650 this.thumbEl.setStyle({
27651 width : width + 'px',
27652 height : height + 'px'
27659 setThumbBoxPosition : function()
27661 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27662 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27664 this.thumbEl.setLeft(x);
27665 this.thumbEl.setTop(y);
27669 baseRotateLevel : function()
27671 this.baseRotate = 1;
27674 typeof(this.exif) != 'undefined' &&
27675 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27676 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27678 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27681 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27685 baseScaleLevel : function()
27689 if(this.isDocument){
27691 if(this.baseRotate == 6 || this.baseRotate == 8){
27693 height = this.thumbEl.getHeight();
27694 this.baseScale = height / this.imageEl.OriginWidth;
27696 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27697 width = this.thumbEl.getWidth();
27698 this.baseScale = width / this.imageEl.OriginHeight;
27704 height = this.thumbEl.getHeight();
27705 this.baseScale = height / this.imageEl.OriginHeight;
27707 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27708 width = this.thumbEl.getWidth();
27709 this.baseScale = width / this.imageEl.OriginWidth;
27715 if(this.baseRotate == 6 || this.baseRotate == 8){
27717 width = this.thumbEl.getHeight();
27718 this.baseScale = width / this.imageEl.OriginHeight;
27720 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27721 height = this.thumbEl.getWidth();
27722 this.baseScale = height / this.imageEl.OriginHeight;
27725 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27726 height = this.thumbEl.getWidth();
27727 this.baseScale = height / this.imageEl.OriginHeight;
27729 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27730 width = this.thumbEl.getHeight();
27731 this.baseScale = width / this.imageEl.OriginWidth;
27738 width = this.thumbEl.getWidth();
27739 this.baseScale = width / this.imageEl.OriginWidth;
27741 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27742 height = this.thumbEl.getHeight();
27743 this.baseScale = height / this.imageEl.OriginHeight;
27746 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27748 height = this.thumbEl.getHeight();
27749 this.baseScale = height / this.imageEl.OriginHeight;
27751 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27752 width = this.thumbEl.getWidth();
27753 this.baseScale = width / this.imageEl.OriginWidth;
27761 getScaleLevel : function()
27763 return this.baseScale * Math.pow(1.1, this.scale);
27766 onTouchStart : function(e)
27768 if(!this.canvasLoaded){
27769 this.beforeSelectFile(e);
27773 var touches = e.browserEvent.touches;
27779 if(touches.length == 1){
27780 this.onMouseDown(e);
27784 if(touches.length != 2){
27790 for(var i = 0, finger; finger = touches[i]; i++){
27791 coords.push(finger.pageX, finger.pageY);
27794 var x = Math.pow(coords[0] - coords[2], 2);
27795 var y = Math.pow(coords[1] - coords[3], 2);
27797 this.startDistance = Math.sqrt(x + y);
27799 this.startScale = this.scale;
27801 this.pinching = true;
27802 this.dragable = false;
27806 onTouchMove : function(e)
27808 if(!this.pinching && !this.dragable){
27812 var touches = e.browserEvent.touches;
27819 this.onMouseMove(e);
27825 for(var i = 0, finger; finger = touches[i]; i++){
27826 coords.push(finger.pageX, finger.pageY);
27829 var x = Math.pow(coords[0] - coords[2], 2);
27830 var y = Math.pow(coords[1] - coords[3], 2);
27832 this.endDistance = Math.sqrt(x + y);
27834 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
27836 if(!this.zoomable()){
27837 this.scale = this.startScale;
27845 onTouchEnd : function(e)
27847 this.pinching = false;
27848 this.dragable = false;
27852 process : function(file, crop)
27855 this.maskEl.mask(this.loadingText);
27858 this.xhr = new XMLHttpRequest();
27860 file.xhr = this.xhr;
27862 this.xhr.open(this.method, this.url, true);
27865 "Accept": "application/json",
27866 "Cache-Control": "no-cache",
27867 "X-Requested-With": "XMLHttpRequest"
27870 for (var headerName in headers) {
27871 var headerValue = headers[headerName];
27873 this.xhr.setRequestHeader(headerName, headerValue);
27879 this.xhr.onload = function()
27881 _this.xhrOnLoad(_this.xhr);
27884 this.xhr.onerror = function()
27886 _this.xhrOnError(_this.xhr);
27889 var formData = new FormData();
27891 formData.append('returnHTML', 'NO');
27894 formData.append('crop', crop);
27897 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
27898 formData.append(this.paramName, file, file.name);
27901 if(typeof(file.filename) != 'undefined'){
27902 formData.append('filename', file.filename);
27905 if(typeof(file.mimetype) != 'undefined'){
27906 formData.append('mimetype', file.mimetype);
27909 if(this.fireEvent('arrange', this, formData) != false){
27910 this.xhr.send(formData);
27914 xhrOnLoad : function(xhr)
27917 this.maskEl.unmask();
27920 if (xhr.readyState !== 4) {
27921 this.fireEvent('exception', this, xhr);
27925 var response = Roo.decode(xhr.responseText);
27927 if(!response.success){
27928 this.fireEvent('exception', this, xhr);
27932 var response = Roo.decode(xhr.responseText);
27934 this.fireEvent('upload', this, response);
27938 xhrOnError : function()
27941 this.maskEl.unmask();
27944 Roo.log('xhr on error');
27946 var response = Roo.decode(xhr.responseText);
27952 prepare : function(file)
27955 this.maskEl.mask(this.loadingText);
27961 if(typeof(file) === 'string'){
27962 this.loadCanvas(file);
27966 if(!file || !this.urlAPI){
27971 this.cropType = file.type;
27975 if(this.fireEvent('prepare', this, this.file) != false){
27977 var reader = new FileReader();
27979 reader.onload = function (e) {
27980 if (e.target.error) {
27981 Roo.log(e.target.error);
27985 var buffer = e.target.result,
27986 dataView = new DataView(buffer),
27988 maxOffset = dataView.byteLength - 4,
27992 if (dataView.getUint16(0) === 0xffd8) {
27993 while (offset < maxOffset) {
27994 markerBytes = dataView.getUint16(offset);
27996 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
27997 markerLength = dataView.getUint16(offset + 2) + 2;
27998 if (offset + markerLength > dataView.byteLength) {
27999 Roo.log('Invalid meta data: Invalid segment size.');
28003 if(markerBytes == 0xffe1){
28004 _this.parseExifData(
28011 offset += markerLength;
28021 var url = _this.urlAPI.createObjectURL(_this.file);
28023 _this.loadCanvas(url);
28028 reader.readAsArrayBuffer(this.file);
28034 parseExifData : function(dataView, offset, length)
28036 var tiffOffset = offset + 10,
28040 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28041 // No Exif data, might be XMP data instead
28045 // Check for the ASCII code for "Exif" (0x45786966):
28046 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28047 // No Exif data, might be XMP data instead
28050 if (tiffOffset + 8 > dataView.byteLength) {
28051 Roo.log('Invalid Exif data: Invalid segment size.');
28054 // Check for the two null bytes:
28055 if (dataView.getUint16(offset + 8) !== 0x0000) {
28056 Roo.log('Invalid Exif data: Missing byte alignment offset.');
28059 // Check the byte alignment:
28060 switch (dataView.getUint16(tiffOffset)) {
28062 littleEndian = true;
28065 littleEndian = false;
28068 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28071 // Check for the TIFF tag marker (0x002A):
28072 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28073 Roo.log('Invalid Exif data: Missing TIFF marker.');
28076 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28077 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28079 this.parseExifTags(
28082 tiffOffset + dirOffset,
28087 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28092 if (dirOffset + 6 > dataView.byteLength) {
28093 Roo.log('Invalid Exif data: Invalid directory offset.');
28096 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28097 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28098 if (dirEndOffset + 4 > dataView.byteLength) {
28099 Roo.log('Invalid Exif data: Invalid directory size.');
28102 for (i = 0; i < tagsNumber; i += 1) {
28106 dirOffset + 2 + 12 * i, // tag offset
28110 // Return the offset to the next directory:
28111 return dataView.getUint32(dirEndOffset, littleEndian);
28114 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
28116 var tag = dataView.getUint16(offset, littleEndian);
28118 this.exif[tag] = this.getExifValue(
28122 dataView.getUint16(offset + 2, littleEndian), // tag type
28123 dataView.getUint32(offset + 4, littleEndian), // tag length
28128 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28130 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28139 Roo.log('Invalid Exif data: Invalid tag type.');
28143 tagSize = tagType.size * length;
28144 // Determine if the value is contained in the dataOffset bytes,
28145 // or if the value at the dataOffset is a pointer to the actual data:
28146 dataOffset = tagSize > 4 ?
28147 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28148 if (dataOffset + tagSize > dataView.byteLength) {
28149 Roo.log('Invalid Exif data: Invalid data offset.');
28152 if (length === 1) {
28153 return tagType.getValue(dataView, dataOffset, littleEndian);
28156 for (i = 0; i < length; i += 1) {
28157 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28160 if (tagType.ascii) {
28162 // Concatenate the chars:
28163 for (i = 0; i < values.length; i += 1) {
28165 // Ignore the terminating NULL byte(s):
28166 if (c === '\u0000') {
28178 Roo.apply(Roo.bootstrap.UploadCropbox, {
28180 'Orientation': 0x0112
28184 1: 0, //'top-left',
28186 3: 180, //'bottom-right',
28187 // 4: 'bottom-left',
28189 6: 90, //'right-top',
28190 // 7: 'right-bottom',
28191 8: 270 //'left-bottom'
28195 // byte, 8-bit unsigned int:
28197 getValue: function (dataView, dataOffset) {
28198 return dataView.getUint8(dataOffset);
28202 // ascii, 8-bit byte:
28204 getValue: function (dataView, dataOffset) {
28205 return String.fromCharCode(dataView.getUint8(dataOffset));
28210 // short, 16 bit int:
28212 getValue: function (dataView, dataOffset, littleEndian) {
28213 return dataView.getUint16(dataOffset, littleEndian);
28217 // long, 32 bit int:
28219 getValue: function (dataView, dataOffset, littleEndian) {
28220 return dataView.getUint32(dataOffset, littleEndian);
28224 // rational = two long values, first is numerator, second is denominator:
28226 getValue: function (dataView, dataOffset, littleEndian) {
28227 return dataView.getUint32(dataOffset, littleEndian) /
28228 dataView.getUint32(dataOffset + 4, littleEndian);
28232 // slong, 32 bit signed int:
28234 getValue: function (dataView, dataOffset, littleEndian) {
28235 return dataView.getInt32(dataOffset, littleEndian);
28239 // srational, two slongs, first is numerator, second is denominator:
28241 getValue: function (dataView, dataOffset, littleEndian) {
28242 return dataView.getInt32(dataOffset, littleEndian) /
28243 dataView.getInt32(dataOffset + 4, littleEndian);
28253 cls : 'btn-group roo-upload-cropbox-rotate-left',
28254 action : 'rotate-left',
28258 cls : 'btn btn-default',
28259 html : '<i class="fa fa-undo"></i>'
28265 cls : 'btn-group roo-upload-cropbox-picture',
28266 action : 'picture',
28270 cls : 'btn btn-default',
28271 html : '<i class="fa fa-picture-o"></i>'
28277 cls : 'btn-group roo-upload-cropbox-rotate-right',
28278 action : 'rotate-right',
28282 cls : 'btn btn-default',
28283 html : '<i class="fa fa-repeat"></i>'
28291 cls : 'btn-group roo-upload-cropbox-rotate-left',
28292 action : 'rotate-left',
28296 cls : 'btn btn-default',
28297 html : '<i class="fa fa-undo"></i>'
28303 cls : 'btn-group roo-upload-cropbox-download',
28304 action : 'download',
28308 cls : 'btn btn-default',
28309 html : '<i class="fa fa-download"></i>'
28315 cls : 'btn-group roo-upload-cropbox-crop',
28320 cls : 'btn btn-default',
28321 html : '<i class="fa fa-crop"></i>'
28327 cls : 'btn-group roo-upload-cropbox-trash',
28332 cls : 'btn btn-default',
28333 html : '<i class="fa fa-trash"></i>'
28339 cls : 'btn-group roo-upload-cropbox-rotate-right',
28340 action : 'rotate-right',
28344 cls : 'btn btn-default',
28345 html : '<i class="fa fa-repeat"></i>'
28353 cls : 'btn-group roo-upload-cropbox-rotate-left',
28354 action : 'rotate-left',
28358 cls : 'btn btn-default',
28359 html : '<i class="fa fa-undo"></i>'
28365 cls : 'btn-group roo-upload-cropbox-rotate-right',
28366 action : 'rotate-right',
28370 cls : 'btn btn-default',
28371 html : '<i class="fa fa-repeat"></i>'
28384 * @class Roo.bootstrap.DocumentManager
28385 * @extends Roo.bootstrap.Component
28386 * Bootstrap DocumentManager class
28387 * @cfg {String} paramName default 'imageUpload'
28388 * @cfg {String} toolTipName default 'filename'
28389 * @cfg {String} method default POST
28390 * @cfg {String} url action url
28391 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28392 * @cfg {Boolean} multiple multiple upload default true
28393 * @cfg {Number} thumbSize default 300
28394 * @cfg {String} fieldLabel
28395 * @cfg {Number} labelWidth default 4
28396 * @cfg {String} labelAlign (left|top) default left
28397 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28398 * @cfg {Number} labellg set the width of label (1-12)
28399 * @cfg {Number} labelmd set the width of label (1-12)
28400 * @cfg {Number} labelsm set the width of label (1-12)
28401 * @cfg {Number} labelxs set the width of label (1-12)
28404 * Create a new DocumentManager
28405 * @param {Object} config The config object
28408 Roo.bootstrap.DocumentManager = function(config){
28409 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28412 this.delegates = [];
28417 * Fire when initial the DocumentManager
28418 * @param {Roo.bootstrap.DocumentManager} this
28423 * inspect selected file
28424 * @param {Roo.bootstrap.DocumentManager} this
28425 * @param {File} file
28430 * Fire when xhr load exception
28431 * @param {Roo.bootstrap.DocumentManager} this
28432 * @param {XMLHttpRequest} xhr
28434 "exception" : true,
28436 * @event afterupload
28437 * Fire when xhr load exception
28438 * @param {Roo.bootstrap.DocumentManager} this
28439 * @param {XMLHttpRequest} xhr
28441 "afterupload" : true,
28444 * prepare the form data
28445 * @param {Roo.bootstrap.DocumentManager} this
28446 * @param {Object} formData
28451 * Fire when remove the file
28452 * @param {Roo.bootstrap.DocumentManager} this
28453 * @param {Object} file
28458 * Fire after refresh the file
28459 * @param {Roo.bootstrap.DocumentManager} this
28464 * Fire after click the image
28465 * @param {Roo.bootstrap.DocumentManager} this
28466 * @param {Object} file
28471 * Fire when upload a image and editable set to true
28472 * @param {Roo.bootstrap.DocumentManager} this
28473 * @param {Object} file
28477 * @event beforeselectfile
28478 * Fire before select file
28479 * @param {Roo.bootstrap.DocumentManager} this
28481 "beforeselectfile" : true,
28484 * Fire before process file
28485 * @param {Roo.bootstrap.DocumentManager} this
28486 * @param {Object} file
28490 * @event previewrendered
28491 * Fire when preview rendered
28492 * @param {Roo.bootstrap.DocumentManager} this
28493 * @param {Object} file
28495 "previewrendered" : true
28500 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
28509 paramName : 'imageUpload',
28510 toolTipName : 'filename',
28513 labelAlign : 'left',
28523 getAutoCreate : function()
28525 var managerWidget = {
28527 cls : 'roo-document-manager',
28531 cls : 'roo-document-manager-selector',
28536 cls : 'roo-document-manager-uploader',
28540 cls : 'roo-document-manager-upload-btn',
28541 html : '<i class="fa fa-plus"></i>'
28552 cls : 'column col-md-12',
28557 if(this.fieldLabel.length){
28562 cls : 'column col-md-12',
28563 html : this.fieldLabel
28567 cls : 'column col-md-12',
28572 if(this.labelAlign == 'left'){
28577 html : this.fieldLabel
28586 if(this.labelWidth > 12){
28587 content[0].style = "width: " + this.labelWidth + 'px';
28590 if(this.labelWidth < 13 && this.labelmd == 0){
28591 this.labelmd = this.labelWidth;
28594 if(this.labellg > 0){
28595 content[0].cls += ' col-lg-' + this.labellg;
28596 content[1].cls += ' col-lg-' + (12 - this.labellg);
28599 if(this.labelmd > 0){
28600 content[0].cls += ' col-md-' + this.labelmd;
28601 content[1].cls += ' col-md-' + (12 - this.labelmd);
28604 if(this.labelsm > 0){
28605 content[0].cls += ' col-sm-' + this.labelsm;
28606 content[1].cls += ' col-sm-' + (12 - this.labelsm);
28609 if(this.labelxs > 0){
28610 content[0].cls += ' col-xs-' + this.labelxs;
28611 content[1].cls += ' col-xs-' + (12 - this.labelxs);
28619 cls : 'row clearfix',
28627 initEvents : function()
28629 this.managerEl = this.el.select('.roo-document-manager', true).first();
28630 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28632 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28633 this.selectorEl.hide();
28636 this.selectorEl.attr('multiple', 'multiple');
28639 this.selectorEl.on('change', this.onFileSelected, this);
28641 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28642 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28644 this.uploader.on('click', this.onUploaderClick, this);
28646 this.renderProgressDialog();
28650 window.addEventListener("resize", function() { _this.refresh(); } );
28652 this.fireEvent('initial', this);
28655 renderProgressDialog : function()
28659 this.progressDialog = new Roo.bootstrap.Modal({
28660 cls : 'roo-document-manager-progress-dialog',
28661 allow_close : false,
28671 btnclick : function() {
28672 _this.uploadCancel();
28678 this.progressDialog.render(Roo.get(document.body));
28680 this.progress = new Roo.bootstrap.Progress({
28681 cls : 'roo-document-manager-progress',
28686 this.progress.render(this.progressDialog.getChildContainer());
28688 this.progressBar = new Roo.bootstrap.ProgressBar({
28689 cls : 'roo-document-manager-progress-bar',
28692 aria_valuemax : 12,
28696 this.progressBar.render(this.progress.getChildContainer());
28699 onUploaderClick : function(e)
28701 e.preventDefault();
28703 if(this.fireEvent('beforeselectfile', this) != false){
28704 this.selectorEl.dom.click();
28709 onFileSelected : function(e)
28711 e.preventDefault();
28713 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28717 Roo.each(this.selectorEl.dom.files, function(file){
28718 if(this.fireEvent('inspect', this, file) != false){
28719 this.files.push(file);
28729 this.selectorEl.dom.value = '';
28731 if(!this.files || !this.files.length){
28735 if(this.boxes > 0 && this.files.length > this.boxes){
28736 this.files = this.files.slice(0, this.boxes);
28739 this.uploader.show();
28741 if(this.boxes > 0 && this.files.length > this.boxes - 1){
28742 this.uploader.hide();
28751 Roo.each(this.files, function(file){
28753 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28754 var f = this.renderPreview(file);
28759 if(file.type.indexOf('image') != -1){
28760 this.delegates.push(
28762 _this.process(file);
28763 }).createDelegate(this)
28771 _this.process(file);
28772 }).createDelegate(this)
28777 this.files = files;
28779 this.delegates = this.delegates.concat(docs);
28781 if(!this.delegates.length){
28786 this.progressBar.aria_valuemax = this.delegates.length;
28793 arrange : function()
28795 if(!this.delegates.length){
28796 this.progressDialog.hide();
28801 var delegate = this.delegates.shift();
28803 this.progressDialog.show();
28805 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
28807 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
28812 refresh : function()
28814 this.uploader.show();
28816 if(this.boxes > 0 && this.files.length > this.boxes - 1){
28817 this.uploader.hide();
28820 Roo.isTouch ? this.closable(false) : this.closable(true);
28822 this.fireEvent('refresh', this);
28825 onRemove : function(e, el, o)
28827 e.preventDefault();
28829 this.fireEvent('remove', this, o);
28833 remove : function(o)
28837 Roo.each(this.files, function(file){
28838 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
28847 this.files = files;
28854 Roo.each(this.files, function(file){
28859 file.target.remove();
28868 onClick : function(e, el, o)
28870 e.preventDefault();
28872 this.fireEvent('click', this, o);
28876 closable : function(closable)
28878 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
28880 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28892 xhrOnLoad : function(xhr)
28894 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28898 if (xhr.readyState !== 4) {
28900 this.fireEvent('exception', this, xhr);
28904 var response = Roo.decode(xhr.responseText);
28906 if(!response.success){
28908 this.fireEvent('exception', this, xhr);
28912 var file = this.renderPreview(response.data);
28914 this.files.push(file);
28918 this.fireEvent('afterupload', this, xhr);
28922 xhrOnError : function(xhr)
28924 Roo.log('xhr on error');
28926 var response = Roo.decode(xhr.responseText);
28933 process : function(file)
28935 if(this.fireEvent('process', this, file) !== false){
28936 if(this.editable && file.type.indexOf('image') != -1){
28937 this.fireEvent('edit', this, file);
28941 this.uploadStart(file, false);
28948 uploadStart : function(file, crop)
28950 this.xhr = new XMLHttpRequest();
28952 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28957 file.xhr = this.xhr;
28959 this.managerEl.createChild({
28961 cls : 'roo-document-manager-loading',
28965 tooltip : file.name,
28966 cls : 'roo-document-manager-thumb',
28967 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28973 this.xhr.open(this.method, this.url, true);
28976 "Accept": "application/json",
28977 "Cache-Control": "no-cache",
28978 "X-Requested-With": "XMLHttpRequest"
28981 for (var headerName in headers) {
28982 var headerValue = headers[headerName];
28984 this.xhr.setRequestHeader(headerName, headerValue);
28990 this.xhr.onload = function()
28992 _this.xhrOnLoad(_this.xhr);
28995 this.xhr.onerror = function()
28997 _this.xhrOnError(_this.xhr);
29000 var formData = new FormData();
29002 formData.append('returnHTML', 'NO');
29005 formData.append('crop', crop);
29008 formData.append(this.paramName, file, file.name);
29015 if(this.fireEvent('prepare', this, formData, options) != false){
29017 if(options.manually){
29021 this.xhr.send(formData);
29025 this.uploadCancel();
29028 uploadCancel : function()
29034 this.delegates = [];
29036 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29043 renderPreview : function(file)
29045 if(typeof(file.target) != 'undefined' && file.target){
29049 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29051 var previewEl = this.managerEl.createChild({
29053 cls : 'roo-document-manager-preview',
29057 tooltip : file[this.toolTipName],
29058 cls : 'roo-document-manager-thumb',
29059 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29064 html : '<i class="fa fa-times-circle"></i>'
29069 var close = previewEl.select('button.close', true).first();
29071 close.on('click', this.onRemove, this, file);
29073 file.target = previewEl;
29075 var image = previewEl.select('img', true).first();
29079 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29081 image.on('click', this.onClick, this, file);
29083 this.fireEvent('previewrendered', this, file);
29089 onPreviewLoad : function(file, image)
29091 if(typeof(file.target) == 'undefined' || !file.target){
29095 var width = image.dom.naturalWidth || image.dom.width;
29096 var height = image.dom.naturalHeight || image.dom.height;
29098 if(width > height){
29099 file.target.addClass('wide');
29103 file.target.addClass('tall');
29108 uploadFromSource : function(file, crop)
29110 this.xhr = new XMLHttpRequest();
29112 this.managerEl.createChild({
29114 cls : 'roo-document-manager-loading',
29118 tooltip : file.name,
29119 cls : 'roo-document-manager-thumb',
29120 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29126 this.xhr.open(this.method, this.url, true);
29129 "Accept": "application/json",
29130 "Cache-Control": "no-cache",
29131 "X-Requested-With": "XMLHttpRequest"
29134 for (var headerName in headers) {
29135 var headerValue = headers[headerName];
29137 this.xhr.setRequestHeader(headerName, headerValue);
29143 this.xhr.onload = function()
29145 _this.xhrOnLoad(_this.xhr);
29148 this.xhr.onerror = function()
29150 _this.xhrOnError(_this.xhr);
29153 var formData = new FormData();
29155 formData.append('returnHTML', 'NO');
29157 formData.append('crop', crop);
29159 if(typeof(file.filename) != 'undefined'){
29160 formData.append('filename', file.filename);
29163 if(typeof(file.mimetype) != 'undefined'){
29164 formData.append('mimetype', file.mimetype);
29169 if(this.fireEvent('prepare', this, formData) != false){
29170 this.xhr.send(formData);
29180 * @class Roo.bootstrap.DocumentViewer
29181 * @extends Roo.bootstrap.Component
29182 * Bootstrap DocumentViewer class
29183 * @cfg {Boolean} showDownload (true|false) show download button (default true)
29184 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29187 * Create a new DocumentViewer
29188 * @param {Object} config The config object
29191 Roo.bootstrap.DocumentViewer = function(config){
29192 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29197 * Fire after initEvent
29198 * @param {Roo.bootstrap.DocumentViewer} this
29204 * @param {Roo.bootstrap.DocumentViewer} this
29209 * Fire after download button
29210 * @param {Roo.bootstrap.DocumentViewer} this
29215 * Fire after trash button
29216 * @param {Roo.bootstrap.DocumentViewer} this
29223 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
29225 showDownload : true,
29229 getAutoCreate : function()
29233 cls : 'roo-document-viewer',
29237 cls : 'roo-document-viewer-body',
29241 cls : 'roo-document-viewer-thumb',
29245 cls : 'roo-document-viewer-image'
29253 cls : 'roo-document-viewer-footer',
29256 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29260 cls : 'btn-group roo-document-viewer-download',
29264 cls : 'btn btn-default',
29265 html : '<i class="fa fa-download"></i>'
29271 cls : 'btn-group roo-document-viewer-trash',
29275 cls : 'btn btn-default',
29276 html : '<i class="fa fa-trash"></i>'
29289 initEvents : function()
29291 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29292 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29294 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29295 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29297 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29298 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29300 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29301 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29303 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29304 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29306 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29307 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29309 this.bodyEl.on('click', this.onClick, this);
29310 this.downloadBtn.on('click', this.onDownload, this);
29311 this.trashBtn.on('click', this.onTrash, this);
29313 this.downloadBtn.hide();
29314 this.trashBtn.hide();
29316 if(this.showDownload){
29317 this.downloadBtn.show();
29320 if(this.showTrash){
29321 this.trashBtn.show();
29324 if(!this.showDownload && !this.showTrash) {
29325 this.footerEl.hide();
29330 initial : function()
29332 this.fireEvent('initial', this);
29336 onClick : function(e)
29338 e.preventDefault();
29340 this.fireEvent('click', this);
29343 onDownload : function(e)
29345 e.preventDefault();
29347 this.fireEvent('download', this);
29350 onTrash : function(e)
29352 e.preventDefault();
29354 this.fireEvent('trash', this);
29366 * @class Roo.bootstrap.NavProgressBar
29367 * @extends Roo.bootstrap.Component
29368 * Bootstrap NavProgressBar class
29371 * Create a new nav progress bar
29372 * @param {Object} config The config object
29375 Roo.bootstrap.NavProgressBar = function(config){
29376 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29378 this.bullets = this.bullets || [];
29380 // Roo.bootstrap.NavProgressBar.register(this);
29384 * Fires when the active item changes
29385 * @param {Roo.bootstrap.NavProgressBar} this
29386 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29387 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
29394 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
29399 getAutoCreate : function()
29401 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29405 cls : 'roo-navigation-bar-group',
29409 cls : 'roo-navigation-top-bar'
29413 cls : 'roo-navigation-bullets-bar',
29417 cls : 'roo-navigation-bar'
29424 cls : 'roo-navigation-bottom-bar'
29434 initEvents: function()
29439 onRender : function(ct, position)
29441 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29443 if(this.bullets.length){
29444 Roo.each(this.bullets, function(b){
29453 addItem : function(cfg)
29455 var item = new Roo.bootstrap.NavProgressItem(cfg);
29457 item.parentId = this.id;
29458 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29461 var top = new Roo.bootstrap.Element({
29463 cls : 'roo-navigation-bar-text'
29466 var bottom = new Roo.bootstrap.Element({
29468 cls : 'roo-navigation-bar-text'
29471 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29472 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29474 var topText = new Roo.bootstrap.Element({
29476 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29479 var bottomText = new Roo.bootstrap.Element({
29481 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29484 topText.onRender(top.el, null);
29485 bottomText.onRender(bottom.el, null);
29488 item.bottomEl = bottom;
29491 this.barItems.push(item);
29496 getActive : function()
29498 var active = false;
29500 Roo.each(this.barItems, function(v){
29502 if (!v.isActive()) {
29514 setActiveItem : function(item)
29518 Roo.each(this.barItems, function(v){
29519 if (v.rid == item.rid) {
29523 if (v.isActive()) {
29524 v.setActive(false);
29529 item.setActive(true);
29531 this.fireEvent('changed', this, item, prev);
29534 getBarItem: function(rid)
29538 Roo.each(this.barItems, function(e) {
29539 if (e.rid != rid) {
29550 indexOfItem : function(item)
29554 Roo.each(this.barItems, function(v, i){
29556 if (v.rid != item.rid) {
29567 setActiveNext : function()
29569 var i = this.indexOfItem(this.getActive());
29571 if (i > this.barItems.length) {
29575 this.setActiveItem(this.barItems[i+1]);
29578 setActivePrev : function()
29580 var i = this.indexOfItem(this.getActive());
29586 this.setActiveItem(this.barItems[i-1]);
29589 format : function()
29591 if(!this.barItems.length){
29595 var width = 100 / this.barItems.length;
29597 Roo.each(this.barItems, function(i){
29598 i.el.setStyle('width', width + '%');
29599 i.topEl.el.setStyle('width', width + '%');
29600 i.bottomEl.el.setStyle('width', width + '%');
29609 * Nav Progress Item
29614 * @class Roo.bootstrap.NavProgressItem
29615 * @extends Roo.bootstrap.Component
29616 * Bootstrap NavProgressItem class
29617 * @cfg {String} rid the reference id
29618 * @cfg {Boolean} active (true|false) Is item active default false
29619 * @cfg {Boolean} disabled (true|false) Is item active default false
29620 * @cfg {String} html
29621 * @cfg {String} position (top|bottom) text position default bottom
29622 * @cfg {String} icon show icon instead of number
29625 * Create a new NavProgressItem
29626 * @param {Object} config The config object
29628 Roo.bootstrap.NavProgressItem = function(config){
29629 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29634 * The raw click event for the entire grid.
29635 * @param {Roo.bootstrap.NavProgressItem} this
29636 * @param {Roo.EventObject} e
29643 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
29649 position : 'bottom',
29652 getAutoCreate : function()
29654 var iconCls = 'roo-navigation-bar-item-icon';
29656 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29660 cls: 'roo-navigation-bar-item',
29670 cfg.cls += ' active';
29673 cfg.cls += ' disabled';
29679 disable : function()
29681 this.setDisabled(true);
29684 enable : function()
29686 this.setDisabled(false);
29689 initEvents: function()
29691 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29693 this.iconEl.on('click', this.onClick, this);
29696 onClick : function(e)
29698 e.preventDefault();
29704 if(this.fireEvent('click', this, e) === false){
29708 this.parent().setActiveItem(this);
29711 isActive: function ()
29713 return this.active;
29716 setActive : function(state)
29718 if(this.active == state){
29722 this.active = state;
29725 this.el.addClass('active');
29729 this.el.removeClass('active');
29734 setDisabled : function(state)
29736 if(this.disabled == state){
29740 this.disabled = state;
29743 this.el.addClass('disabled');
29747 this.el.removeClass('disabled');
29750 tooltipEl : function()
29752 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29765 * @class Roo.bootstrap.FieldLabel
29766 * @extends Roo.bootstrap.Component
29767 * Bootstrap FieldLabel class
29768 * @cfg {String} html contents of the element
29769 * @cfg {String} tag tag of the element default label
29770 * @cfg {String} cls class of the element
29771 * @cfg {String} target label target
29772 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
29773 * @cfg {String} invalidClass default "text-warning"
29774 * @cfg {String} validClass default "text-success"
29775 * @cfg {String} iconTooltip default "This field is required"
29776 * @cfg {String} indicatorpos (left|right) default left
29779 * Create a new FieldLabel
29780 * @param {Object} config The config object
29783 Roo.bootstrap.FieldLabel = function(config){
29784 Roo.bootstrap.Element.superclass.constructor.call(this, config);
29789 * Fires after the field has been marked as invalid.
29790 * @param {Roo.form.FieldLabel} this
29791 * @param {String} msg The validation message
29796 * Fires after the field has been validated with no errors.
29797 * @param {Roo.form.FieldLabel} this
29803 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
29810 invalidClass : 'has-warning',
29811 validClass : 'has-success',
29812 iconTooltip : 'This field is required',
29813 indicatorpos : 'left',
29815 getAutoCreate : function(){
29819 cls : 'roo-bootstrap-field-label ' + this.cls,
29824 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
29825 tooltip : this.iconTooltip
29834 if(this.indicatorpos == 'right'){
29837 cls : 'roo-bootstrap-field-label ' + this.cls,
29846 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
29847 tooltip : this.iconTooltip
29856 initEvents: function()
29858 Roo.bootstrap.Element.superclass.initEvents.call(this);
29860 this.indicator = this.indicatorEl();
29862 if(this.indicator){
29863 this.indicator.removeClass('visible');
29864 this.indicator.addClass('invisible');
29867 Roo.bootstrap.FieldLabel.register(this);
29870 indicatorEl : function()
29872 var indicator = this.el.select('i.roo-required-indicator',true).first();
29883 * Mark this field as valid
29885 markValid : function()
29887 if(this.indicator){
29888 this.indicator.removeClass('visible');
29889 this.indicator.addClass('invisible');
29892 this.el.removeClass(this.invalidClass);
29894 this.el.addClass(this.validClass);
29896 this.fireEvent('valid', this);
29900 * Mark this field as invalid
29901 * @param {String} msg The validation message
29903 markInvalid : function(msg)
29905 if(this.indicator){
29906 this.indicator.removeClass('invisible');
29907 this.indicator.addClass('visible');
29910 this.el.removeClass(this.validClass);
29912 this.el.addClass(this.invalidClass);
29914 this.fireEvent('invalid', this, msg);
29920 Roo.apply(Roo.bootstrap.FieldLabel, {
29925 * register a FieldLabel Group
29926 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
29928 register : function(label)
29930 if(this.groups.hasOwnProperty(label.target)){
29934 this.groups[label.target] = label;
29938 * fetch a FieldLabel Group based on the target
29939 * @param {string} target
29940 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
29942 get: function(target) {
29943 if (typeof(this.groups[target]) == 'undefined') {
29947 return this.groups[target] ;
29956 * page DateSplitField.
29962 * @class Roo.bootstrap.DateSplitField
29963 * @extends Roo.bootstrap.Component
29964 * Bootstrap DateSplitField class
29965 * @cfg {string} fieldLabel - the label associated
29966 * @cfg {Number} labelWidth set the width of label (0-12)
29967 * @cfg {String} labelAlign (top|left)
29968 * @cfg {Boolean} dayAllowBlank (true|false) default false
29969 * @cfg {Boolean} monthAllowBlank (true|false) default false
29970 * @cfg {Boolean} yearAllowBlank (true|false) default false
29971 * @cfg {string} dayPlaceholder
29972 * @cfg {string} monthPlaceholder
29973 * @cfg {string} yearPlaceholder
29974 * @cfg {string} dayFormat default 'd'
29975 * @cfg {string} monthFormat default 'm'
29976 * @cfg {string} yearFormat default 'Y'
29977 * @cfg {Number} labellg set the width of label (1-12)
29978 * @cfg {Number} labelmd set the width of label (1-12)
29979 * @cfg {Number} labelsm set the width of label (1-12)
29980 * @cfg {Number} labelxs set the width of label (1-12)
29984 * Create a new DateSplitField
29985 * @param {Object} config The config object
29988 Roo.bootstrap.DateSplitField = function(config){
29989 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
29995 * getting the data of years
29996 * @param {Roo.bootstrap.DateSplitField} this
29997 * @param {Object} years
30002 * getting the data of days
30003 * @param {Roo.bootstrap.DateSplitField} this
30004 * @param {Object} days
30009 * Fires after the field has been marked as invalid.
30010 * @param {Roo.form.Field} this
30011 * @param {String} msg The validation message
30016 * Fires after the field has been validated with no errors.
30017 * @param {Roo.form.Field} this
30023 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
30026 labelAlign : 'top',
30028 dayAllowBlank : false,
30029 monthAllowBlank : false,
30030 yearAllowBlank : false,
30031 dayPlaceholder : '',
30032 monthPlaceholder : '',
30033 yearPlaceholder : '',
30037 isFormField : true,
30043 getAutoCreate : function()
30047 cls : 'row roo-date-split-field-group',
30052 cls : 'form-hidden-field roo-date-split-field-group-value',
30058 var labelCls = 'col-md-12';
30059 var contentCls = 'col-md-4';
30061 if(this.fieldLabel){
30065 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30069 html : this.fieldLabel
30074 if(this.labelAlign == 'left'){
30076 if(this.labelWidth > 12){
30077 label.style = "width: " + this.labelWidth + 'px';
30080 if(this.labelWidth < 13 && this.labelmd == 0){
30081 this.labelmd = this.labelWidth;
30084 if(this.labellg > 0){
30085 labelCls = ' col-lg-' + this.labellg;
30086 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30089 if(this.labelmd > 0){
30090 labelCls = ' col-md-' + this.labelmd;
30091 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30094 if(this.labelsm > 0){
30095 labelCls = ' col-sm-' + this.labelsm;
30096 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30099 if(this.labelxs > 0){
30100 labelCls = ' col-xs-' + this.labelxs;
30101 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30105 label.cls += ' ' + labelCls;
30107 cfg.cn.push(label);
30110 Roo.each(['day', 'month', 'year'], function(t){
30113 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30120 inputEl: function ()
30122 return this.el.select('.roo-date-split-field-group-value', true).first();
30125 onRender : function(ct, position)
30129 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30131 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30133 this.dayField = new Roo.bootstrap.ComboBox({
30134 allowBlank : this.dayAllowBlank,
30135 alwaysQuery : true,
30136 displayField : 'value',
30139 forceSelection : true,
30141 placeholder : this.dayPlaceholder,
30142 selectOnFocus : true,
30143 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30144 triggerAction : 'all',
30146 valueField : 'value',
30147 store : new Roo.data.SimpleStore({
30148 data : (function() {
30150 _this.fireEvent('days', _this, days);
30153 fields : [ 'value' ]
30156 select : function (_self, record, index)
30158 _this.setValue(_this.getValue());
30163 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30165 this.monthField = new Roo.bootstrap.MonthField({
30166 after : '<i class=\"fa fa-calendar\"></i>',
30167 allowBlank : this.monthAllowBlank,
30168 placeholder : this.monthPlaceholder,
30171 render : function (_self)
30173 this.el.select('span.input-group-addon', true).first().on('click', function(e){
30174 e.preventDefault();
30178 select : function (_self, oldvalue, newvalue)
30180 _this.setValue(_this.getValue());
30185 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30187 this.yearField = new Roo.bootstrap.ComboBox({
30188 allowBlank : this.yearAllowBlank,
30189 alwaysQuery : true,
30190 displayField : 'value',
30193 forceSelection : true,
30195 placeholder : this.yearPlaceholder,
30196 selectOnFocus : true,
30197 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30198 triggerAction : 'all',
30200 valueField : 'value',
30201 store : new Roo.data.SimpleStore({
30202 data : (function() {
30204 _this.fireEvent('years', _this, years);
30207 fields : [ 'value' ]
30210 select : function (_self, record, index)
30212 _this.setValue(_this.getValue());
30217 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30220 setValue : function(v, format)
30222 this.inputEl.dom.value = v;
30224 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30226 var d = Date.parseDate(v, f);
30233 this.setDay(d.format(this.dayFormat));
30234 this.setMonth(d.format(this.monthFormat));
30235 this.setYear(d.format(this.yearFormat));
30242 setDay : function(v)
30244 this.dayField.setValue(v);
30245 this.inputEl.dom.value = this.getValue();
30250 setMonth : function(v)
30252 this.monthField.setValue(v, true);
30253 this.inputEl.dom.value = this.getValue();
30258 setYear : function(v)
30260 this.yearField.setValue(v);
30261 this.inputEl.dom.value = this.getValue();
30266 getDay : function()
30268 return this.dayField.getValue();
30271 getMonth : function()
30273 return this.monthField.getValue();
30276 getYear : function()
30278 return this.yearField.getValue();
30281 getValue : function()
30283 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30285 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30295 this.inputEl.dom.value = '';
30300 validate : function()
30302 var d = this.dayField.validate();
30303 var m = this.monthField.validate();
30304 var y = this.yearField.validate();
30309 (!this.dayAllowBlank && !d) ||
30310 (!this.monthAllowBlank && !m) ||
30311 (!this.yearAllowBlank && !y)
30316 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30325 this.markInvalid();
30330 markValid : function()
30333 var label = this.el.select('label', true).first();
30334 var icon = this.el.select('i.fa-star', true).first();
30340 this.fireEvent('valid', this);
30344 * Mark this field as invalid
30345 * @param {String} msg The validation message
30347 markInvalid : function(msg)
30350 var label = this.el.select('label', true).first();
30351 var icon = this.el.select('i.fa-star', true).first();
30353 if(label && !icon){
30354 this.el.select('.roo-date-split-field-label', true).createChild({
30356 cls : 'text-danger fa fa-lg fa-star',
30357 tooltip : 'This field is required',
30358 style : 'margin-right:5px;'
30362 this.fireEvent('invalid', this, msg);
30365 clearInvalid : function()
30367 var label = this.el.select('label', true).first();
30368 var icon = this.el.select('i.fa-star', true).first();
30374 this.fireEvent('valid', this);
30377 getName: function()
30387 * http://masonry.desandro.com
30389 * The idea is to render all the bricks based on vertical width...
30391 * The original code extends 'outlayer' - we might need to use that....
30397 * @class Roo.bootstrap.LayoutMasonry
30398 * @extends Roo.bootstrap.Component
30399 * Bootstrap Layout Masonry class
30402 * Create a new Element
30403 * @param {Object} config The config object
30406 Roo.bootstrap.LayoutMasonry = function(config){
30408 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30412 Roo.bootstrap.LayoutMasonry.register(this);
30418 * Fire after layout the items
30419 * @param {Roo.bootstrap.LayoutMasonry} this
30420 * @param {Roo.EventObject} e
30427 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
30430 * @cfg {Boolean} isLayoutInstant = no animation?
30432 isLayoutInstant : false, // needed?
30435 * @cfg {Number} boxWidth width of the columns
30440 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
30445 * @cfg {Number} padWidth padding below box..
30450 * @cfg {Number} gutter gutter width..
30455 * @cfg {Number} maxCols maximum number of columns
30461 * @cfg {Boolean} isAutoInitial defalut true
30463 isAutoInitial : true,
30468 * @cfg {Boolean} isHorizontal defalut false
30470 isHorizontal : false,
30472 currentSize : null,
30478 bricks: null, //CompositeElement
30482 _isLayoutInited : false,
30484 // isAlternative : false, // only use for vertical layout...
30487 * @cfg {Number} alternativePadWidth padding below box..
30489 alternativePadWidth : 50,
30491 selectedBrick : [],
30493 getAutoCreate : function(){
30495 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30499 cls: 'blog-masonary-wrapper ' + this.cls,
30501 cls : 'mas-boxes masonary'
30508 getChildContainer: function( )
30510 if (this.boxesEl) {
30511 return this.boxesEl;
30514 this.boxesEl = this.el.select('.mas-boxes').first();
30516 return this.boxesEl;
30520 initEvents : function()
30524 if(this.isAutoInitial){
30525 Roo.log('hook children rendered');
30526 this.on('childrenrendered', function() {
30527 Roo.log('children rendered');
30533 initial : function()
30535 this.selectedBrick = [];
30537 this.currentSize = this.el.getBox(true);
30539 Roo.EventManager.onWindowResize(this.resize, this);
30541 if(!this.isAutoInitial){
30549 //this.layout.defer(500,this);
30553 resize : function()
30555 var cs = this.el.getBox(true);
30558 this.currentSize.width == cs.width &&
30559 this.currentSize.x == cs.x &&
30560 this.currentSize.height == cs.height &&
30561 this.currentSize.y == cs.y
30563 Roo.log("no change in with or X or Y");
30567 this.currentSize = cs;
30573 layout : function()
30575 this._resetLayout();
30577 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30579 this.layoutItems( isInstant );
30581 this._isLayoutInited = true;
30583 this.fireEvent('layout', this);
30587 _resetLayout : function()
30589 if(this.isHorizontal){
30590 this.horizontalMeasureColumns();
30594 this.verticalMeasureColumns();
30598 verticalMeasureColumns : function()
30600 this.getContainerWidth();
30602 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30603 // this.colWidth = Math.floor(this.containerWidth * 0.8);
30607 var boxWidth = this.boxWidth + this.padWidth;
30609 if(this.containerWidth < this.boxWidth){
30610 boxWidth = this.containerWidth
30613 var containerWidth = this.containerWidth;
30615 var cols = Math.floor(containerWidth / boxWidth);
30617 this.cols = Math.max( cols, 1 );
30619 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30621 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30623 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30625 this.colWidth = boxWidth + avail - this.padWidth;
30627 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30628 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
30631 horizontalMeasureColumns : function()
30633 this.getContainerWidth();
30635 var boxWidth = this.boxWidth;
30637 if(this.containerWidth < boxWidth){
30638 boxWidth = this.containerWidth;
30641 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30643 this.el.setHeight(boxWidth);
30647 getContainerWidth : function()
30649 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
30652 layoutItems : function( isInstant )
30654 Roo.log(this.bricks);
30656 var items = Roo.apply([], this.bricks);
30658 if(this.isHorizontal){
30659 this._horizontalLayoutItems( items , isInstant );
30663 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30664 // this._verticalAlternativeLayoutItems( items , isInstant );
30668 this._verticalLayoutItems( items , isInstant );
30672 _verticalLayoutItems : function ( items , isInstant)
30674 if ( !items || !items.length ) {
30679 ['xs', 'xs', 'xs', 'tall'],
30680 ['xs', 'xs', 'tall'],
30681 ['xs', 'xs', 'sm'],
30682 ['xs', 'xs', 'xs'],
30688 ['sm', 'xs', 'xs'],
30692 ['tall', 'xs', 'xs', 'xs'],
30693 ['tall', 'xs', 'xs'],
30705 Roo.each(items, function(item, k){
30707 switch (item.size) {
30708 // these layouts take up a full box,
30719 boxes.push([item]);
30742 var filterPattern = function(box, length)
30750 var pattern = box.slice(0, length);
30754 Roo.each(pattern, function(i){
30755 format.push(i.size);
30758 Roo.each(standard, function(s){
30760 if(String(s) != String(format)){
30769 if(!match && length == 1){
30774 filterPattern(box, length - 1);
30778 queue.push(pattern);
30780 box = box.slice(length, box.length);
30782 filterPattern(box, 4);
30788 Roo.each(boxes, function(box, k){
30794 if(box.length == 1){
30799 filterPattern(box, 4);
30803 this._processVerticalLayoutQueue( queue, isInstant );
30807 // _verticalAlternativeLayoutItems : function( items , isInstant )
30809 // if ( !items || !items.length ) {
30813 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
30817 _horizontalLayoutItems : function ( items , isInstant)
30819 if ( !items || !items.length || items.length < 3) {
30825 var eItems = items.slice(0, 3);
30827 items = items.slice(3, items.length);
30830 ['xs', 'xs', 'xs', 'wide'],
30831 ['xs', 'xs', 'wide'],
30832 ['xs', 'xs', 'sm'],
30833 ['xs', 'xs', 'xs'],
30839 ['sm', 'xs', 'xs'],
30843 ['wide', 'xs', 'xs', 'xs'],
30844 ['wide', 'xs', 'xs'],
30857 Roo.each(items, function(item, k){
30859 switch (item.size) {
30870 boxes.push([item]);
30894 var filterPattern = function(box, length)
30902 var pattern = box.slice(0, length);
30906 Roo.each(pattern, function(i){
30907 format.push(i.size);
30910 Roo.each(standard, function(s){
30912 if(String(s) != String(format)){
30921 if(!match && length == 1){
30926 filterPattern(box, length - 1);
30930 queue.push(pattern);
30932 box = box.slice(length, box.length);
30934 filterPattern(box, 4);
30940 Roo.each(boxes, function(box, k){
30946 if(box.length == 1){
30951 filterPattern(box, 4);
30958 var pos = this.el.getBox(true);
30962 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30964 var hit_end = false;
30966 Roo.each(queue, function(box){
30970 Roo.each(box, function(b){
30972 b.el.setVisibilityMode(Roo.Element.DISPLAY);
30982 Roo.each(box, function(b){
30984 b.el.setVisibilityMode(Roo.Element.DISPLAY);
30987 mx = Math.max(mx, b.x);
30991 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
30995 Roo.each(box, function(b){
30997 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31011 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31014 /** Sets position of item in DOM
31015 * @param {Element} item
31016 * @param {Number} x - horizontal position
31017 * @param {Number} y - vertical position
31018 * @param {Boolean} isInstant - disables transitions
31020 _processVerticalLayoutQueue : function( queue, isInstant )
31022 var pos = this.el.getBox(true);
31027 for (var i = 0; i < this.cols; i++){
31031 Roo.each(queue, function(box, k){
31033 var col = k % this.cols;
31035 Roo.each(box, function(b,kk){
31037 b.el.position('absolute');
31039 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31040 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31042 if(b.size == 'md-left' || b.size == 'md-right'){
31043 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31044 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31047 b.el.setWidth(width);
31048 b.el.setHeight(height);
31050 b.el.select('iframe',true).setSize(width,height);
31054 for (var i = 0; i < this.cols; i++){
31056 if(maxY[i] < maxY[col]){
31061 col = Math.min(col, i);
31065 x = pos.x + col * (this.colWidth + this.padWidth);
31069 var positions = [];
31071 switch (box.length){
31073 positions = this.getVerticalOneBoxColPositions(x, y, box);
31076 positions = this.getVerticalTwoBoxColPositions(x, y, box);
31079 positions = this.getVerticalThreeBoxColPositions(x, y, box);
31082 positions = this.getVerticalFourBoxColPositions(x, y, box);
31088 Roo.each(box, function(b,kk){
31090 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31092 var sz = b.el.getSize();
31094 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31102 for (var i = 0; i < this.cols; i++){
31103 mY = Math.max(mY, maxY[i]);
31106 this.el.setHeight(mY - pos.y);
31110 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31112 // var pos = this.el.getBox(true);
31115 // var maxX = pos.right;
31117 // var maxHeight = 0;
31119 // Roo.each(items, function(item, k){
31123 // item.el.position('absolute');
31125 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31127 // item.el.setWidth(width);
31129 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31131 // item.el.setHeight(height);
31134 // item.el.setXY([x, y], isInstant ? false : true);
31136 // item.el.setXY([maxX - width, y], isInstant ? false : true);
31139 // y = y + height + this.alternativePadWidth;
31141 // maxHeight = maxHeight + height + this.alternativePadWidth;
31145 // this.el.setHeight(maxHeight);
31149 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31151 var pos = this.el.getBox(true);
31156 var maxX = pos.right;
31158 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31160 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31162 Roo.each(queue, function(box, k){
31164 Roo.each(box, function(b, kk){
31166 b.el.position('absolute');
31168 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31169 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31171 if(b.size == 'md-left' || b.size == 'md-right'){
31172 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31173 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31176 b.el.setWidth(width);
31177 b.el.setHeight(height);
31185 var positions = [];
31187 switch (box.length){
31189 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31192 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31195 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31198 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31204 Roo.each(box, function(b,kk){
31206 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31208 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31216 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31218 Roo.each(eItems, function(b,k){
31220 b.size = (k == 0) ? 'sm' : 'xs';
31221 b.x = (k == 0) ? 2 : 1;
31222 b.y = (k == 0) ? 2 : 1;
31224 b.el.position('absolute');
31226 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31228 b.el.setWidth(width);
31230 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31232 b.el.setHeight(height);
31236 var positions = [];
31239 x : maxX - this.unitWidth * 2 - this.gutter,
31244 x : maxX - this.unitWidth,
31245 y : minY + (this.unitWidth + this.gutter) * 2
31249 x : maxX - this.unitWidth * 3 - this.gutter * 2,
31253 Roo.each(eItems, function(b,k){
31255 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31261 getVerticalOneBoxColPositions : function(x, y, box)
31265 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31267 if(box[0].size == 'md-left'){
31271 if(box[0].size == 'md-right'){
31276 x : x + (this.unitWidth + this.gutter) * rand,
31283 getVerticalTwoBoxColPositions : function(x, y, box)
31287 if(box[0].size == 'xs'){
31291 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31295 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31309 x : x + (this.unitWidth + this.gutter) * 2,
31310 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31317 getVerticalThreeBoxColPositions : function(x, y, box)
31321 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31329 x : x + (this.unitWidth + this.gutter) * 1,
31334 x : x + (this.unitWidth + this.gutter) * 2,
31342 if(box[0].size == 'xs' && box[1].size == 'xs'){
31351 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31355 x : x + (this.unitWidth + this.gutter) * 1,
31369 x : x + (this.unitWidth + this.gutter) * 2,
31374 x : x + (this.unitWidth + this.gutter) * 2,
31375 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31382 getVerticalFourBoxColPositions : function(x, y, box)
31386 if(box[0].size == 'xs'){
31395 y : y + (this.unitHeight + this.gutter) * 1
31400 y : y + (this.unitHeight + this.gutter) * 2
31404 x : x + (this.unitWidth + this.gutter) * 1,
31418 x : x + (this.unitWidth + this.gutter) * 2,
31423 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31424 y : y + (this.unitHeight + this.gutter) * 1
31428 x : x + (this.unitWidth + this.gutter) * 2,
31429 y : y + (this.unitWidth + this.gutter) * 2
31436 getHorizontalOneBoxColPositions : function(maxX, minY, box)
31440 if(box[0].size == 'md-left'){
31442 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31449 if(box[0].size == 'md-right'){
31451 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31452 y : minY + (this.unitWidth + this.gutter) * 1
31458 var rand = Math.floor(Math.random() * (4 - box[0].y));
31461 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31462 y : minY + (this.unitWidth + this.gutter) * rand
31469 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31473 if(box[0].size == 'xs'){
31476 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31481 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31482 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31490 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31495 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31496 y : minY + (this.unitWidth + this.gutter) * 2
31503 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31507 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31510 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31515 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31516 y : minY + (this.unitWidth + this.gutter) * 1
31520 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31521 y : minY + (this.unitWidth + this.gutter) * 2
31528 if(box[0].size == 'xs' && box[1].size == 'xs'){
31531 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31536 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31541 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31542 y : minY + (this.unitWidth + this.gutter) * 1
31550 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31555 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31556 y : minY + (this.unitWidth + this.gutter) * 2
31560 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31561 y : minY + (this.unitWidth + this.gutter) * 2
31568 getHorizontalFourBoxColPositions : function(maxX, minY, box)
31572 if(box[0].size == 'xs'){
31575 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31580 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31585 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),
31590 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31591 y : minY + (this.unitWidth + this.gutter) * 1
31599 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31604 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31605 y : minY + (this.unitWidth + this.gutter) * 2
31609 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31610 y : minY + (this.unitWidth + this.gutter) * 2
31614 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),
31615 y : minY + (this.unitWidth + this.gutter) * 2
31623 * remove a Masonry Brick
31624 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31626 removeBrick : function(brick_id)
31632 for (var i = 0; i<this.bricks.length; i++) {
31633 if (this.bricks[i].id == brick_id) {
31634 this.bricks.splice(i,1);
31635 this.el.dom.removeChild(Roo.get(brick_id).dom);
31642 * adds a Masonry Brick
31643 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31645 addBrick : function(cfg)
31647 var cn = new Roo.bootstrap.MasonryBrick(cfg);
31648 //this.register(cn);
31649 cn.parentId = this.id;
31650 cn.onRender(this.el, null);
31655 * register a Masonry Brick
31656 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31659 register : function(brick)
31661 this.bricks.push(brick);
31662 brick.masonryId = this.id;
31666 * clear all the Masonry Brick
31668 clearAll : function()
31671 //this.getChildContainer().dom.innerHTML = "";
31672 this.el.dom.innerHTML = '';
31675 getSelected : function()
31677 if (!this.selectedBrick) {
31681 return this.selectedBrick;
31685 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31689 * register a Masonry Layout
31690 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31693 register : function(layout)
31695 this.groups[layout.id] = layout;
31698 * fetch a Masonry Layout based on the masonry layout ID
31699 * @param {string} the masonry layout to add
31700 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31703 get: function(layout_id) {
31704 if (typeof(this.groups[layout_id]) == 'undefined') {
31707 return this.groups[layout_id] ;
31719 * http://masonry.desandro.com
31721 * The idea is to render all the bricks based on vertical width...
31723 * The original code extends 'outlayer' - we might need to use that....
31729 * @class Roo.bootstrap.LayoutMasonryAuto
31730 * @extends Roo.bootstrap.Component
31731 * Bootstrap Layout Masonry class
31734 * Create a new Element
31735 * @param {Object} config The config object
31738 Roo.bootstrap.LayoutMasonryAuto = function(config){
31739 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31742 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
31745 * @cfg {Boolean} isFitWidth - resize the width..
31747 isFitWidth : false, // options..
31749 * @cfg {Boolean} isOriginLeft = left align?
31751 isOriginLeft : true,
31753 * @cfg {Boolean} isOriginTop = top align?
31755 isOriginTop : false,
31757 * @cfg {Boolean} isLayoutInstant = no animation?
31759 isLayoutInstant : false, // needed?
31761 * @cfg {Boolean} isResizingContainer = not sure if this is used..
31763 isResizingContainer : true,
31765 * @cfg {Number} columnWidth width of the columns
31771 * @cfg {Number} maxCols maximum number of columns
31776 * @cfg {Number} padHeight padding below box..
31782 * @cfg {Boolean} isAutoInitial defalut true
31785 isAutoInitial : true,
31791 initialColumnWidth : 0,
31792 currentSize : null,
31794 colYs : null, // array.
31801 bricks: null, //CompositeElement
31802 cols : 0, // array?
31803 // element : null, // wrapped now this.el
31804 _isLayoutInited : null,
31807 getAutoCreate : function(){
31811 cls: 'blog-masonary-wrapper ' + this.cls,
31813 cls : 'mas-boxes masonary'
31820 getChildContainer: function( )
31822 if (this.boxesEl) {
31823 return this.boxesEl;
31826 this.boxesEl = this.el.select('.mas-boxes').first();
31828 return this.boxesEl;
31832 initEvents : function()
31836 if(this.isAutoInitial){
31837 Roo.log('hook children rendered');
31838 this.on('childrenrendered', function() {
31839 Roo.log('children rendered');
31846 initial : function()
31848 this.reloadItems();
31850 this.currentSize = this.el.getBox(true);
31852 /// was window resize... - let's see if this works..
31853 Roo.EventManager.onWindowResize(this.resize, this);
31855 if(!this.isAutoInitial){
31860 this.layout.defer(500,this);
31863 reloadItems: function()
31865 this.bricks = this.el.select('.masonry-brick', true);
31867 this.bricks.each(function(b) {
31868 //Roo.log(b.getSize());
31869 if (!b.attr('originalwidth')) {
31870 b.attr('originalwidth', b.getSize().width);
31875 Roo.log(this.bricks.elements.length);
31878 resize : function()
31881 var cs = this.el.getBox(true);
31883 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
31884 Roo.log("no change in with or X");
31887 this.currentSize = cs;
31891 layout : function()
31894 this._resetLayout();
31895 //this._manageStamps();
31897 // don't animate first layout
31898 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31899 this.layoutItems( isInstant );
31901 // flag for initalized
31902 this._isLayoutInited = true;
31905 layoutItems : function( isInstant )
31907 //var items = this._getItemsForLayout( this.items );
31908 // original code supports filtering layout items.. we just ignore it..
31910 this._layoutItems( this.bricks , isInstant );
31912 this._postLayout();
31914 _layoutItems : function ( items , isInstant)
31916 //this.fireEvent( 'layout', this, items );
31919 if ( !items || !items.elements.length ) {
31920 // no items, emit event with empty array
31925 items.each(function(item) {
31926 Roo.log("layout item");
31928 // get x/y object from method
31929 var position = this._getItemLayoutPosition( item );
31931 position.item = item;
31932 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
31933 queue.push( position );
31936 this._processLayoutQueue( queue );
31938 /** Sets position of item in DOM
31939 * @param {Element} item
31940 * @param {Number} x - horizontal position
31941 * @param {Number} y - vertical position
31942 * @param {Boolean} isInstant - disables transitions
31944 _processLayoutQueue : function( queue )
31946 for ( var i=0, len = queue.length; i < len; i++ ) {
31947 var obj = queue[i];
31948 obj.item.position('absolute');
31949 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
31955 * Any logic you want to do after each layout,
31956 * i.e. size the container
31958 _postLayout : function()
31960 this.resizeContainer();
31963 resizeContainer : function()
31965 if ( !this.isResizingContainer ) {
31968 var size = this._getContainerSize();
31970 this.el.setSize(size.width,size.height);
31971 this.boxesEl.setSize(size.width,size.height);
31977 _resetLayout : function()
31979 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
31980 this.colWidth = this.el.getWidth();
31981 //this.gutter = this.el.getWidth();
31983 this.measureColumns();
31989 this.colYs.push( 0 );
31995 measureColumns : function()
31997 this.getContainerWidth();
31998 // if columnWidth is 0, default to outerWidth of first item
31999 if ( !this.columnWidth ) {
32000 var firstItem = this.bricks.first();
32001 Roo.log(firstItem);
32002 this.columnWidth = this.containerWidth;
32003 if (firstItem && firstItem.attr('originalwidth') ) {
32004 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32006 // columnWidth fall back to item of first element
32007 Roo.log("set column width?");
32008 this.initialColumnWidth = this.columnWidth ;
32010 // if first elem has no width, default to size of container
32015 if (this.initialColumnWidth) {
32016 this.columnWidth = this.initialColumnWidth;
32021 // column width is fixed at the top - however if container width get's smaller we should
32024 // this bit calcs how man columns..
32026 var columnWidth = this.columnWidth += this.gutter;
32028 // calculate columns
32029 var containerWidth = this.containerWidth + this.gutter;
32031 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32032 // fix rounding errors, typically with gutters
32033 var excess = columnWidth - containerWidth % columnWidth;
32036 // if overshoot is less than a pixel, round up, otherwise floor it
32037 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32038 cols = Math[ mathMethod ]( cols );
32039 this.cols = Math.max( cols, 1 );
32040 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32042 // padding positioning..
32043 var totalColWidth = this.cols * this.columnWidth;
32044 var padavail = this.containerWidth - totalColWidth;
32045 // so for 2 columns - we need 3 'pads'
32047 var padNeeded = (1+this.cols) * this.padWidth;
32049 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32051 this.columnWidth += padExtra
32052 //this.padWidth = Math.floor(padavail / ( this.cols));
32054 // adjust colum width so that padding is fixed??
32056 // we have 3 columns ... total = width * 3
32057 // we have X left over... that should be used by
32059 //if (this.expandC) {
32067 getContainerWidth : function()
32069 /* // container is parent if fit width
32070 var container = this.isFitWidth ? this.element.parentNode : this.element;
32071 // check that this.size and size are there
32072 // IE8 triggers resize on body size change, so they might not be
32074 var size = getSize( container ); //FIXME
32075 this.containerWidth = size && size.innerWidth; //FIXME
32078 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
32082 _getItemLayoutPosition : function( item ) // what is item?
32084 // we resize the item to our columnWidth..
32086 item.setWidth(this.columnWidth);
32087 item.autoBoxAdjust = false;
32089 var sz = item.getSize();
32091 // how many columns does this brick span
32092 var remainder = this.containerWidth % this.columnWidth;
32094 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32095 // round if off by 1 pixel, otherwise use ceil
32096 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
32097 colSpan = Math.min( colSpan, this.cols );
32099 // normally this should be '1' as we dont' currently allow multi width columns..
32101 var colGroup = this._getColGroup( colSpan );
32102 // get the minimum Y value from the columns
32103 var minimumY = Math.min.apply( Math, colGroup );
32104 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32106 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
32108 // position the brick
32110 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32111 y: this.currentSize.y + minimumY + this.padHeight
32115 // apply setHeight to necessary columns
32116 var setHeight = minimumY + sz.height + this.padHeight;
32117 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32119 var setSpan = this.cols + 1 - colGroup.length;
32120 for ( var i = 0; i < setSpan; i++ ) {
32121 this.colYs[ shortColIndex + i ] = setHeight ;
32128 * @param {Number} colSpan - number of columns the element spans
32129 * @returns {Array} colGroup
32131 _getColGroup : function( colSpan )
32133 if ( colSpan < 2 ) {
32134 // if brick spans only one column, use all the column Ys
32139 // how many different places could this brick fit horizontally
32140 var groupCount = this.cols + 1 - colSpan;
32141 // for each group potential horizontal position
32142 for ( var i = 0; i < groupCount; i++ ) {
32143 // make an array of colY values for that one group
32144 var groupColYs = this.colYs.slice( i, i + colSpan );
32145 // and get the max value of the array
32146 colGroup[i] = Math.max.apply( Math, groupColYs );
32151 _manageStamp : function( stamp )
32153 var stampSize = stamp.getSize();
32154 var offset = stamp.getBox();
32155 // get the columns that this stamp affects
32156 var firstX = this.isOriginLeft ? offset.x : offset.right;
32157 var lastX = firstX + stampSize.width;
32158 var firstCol = Math.floor( firstX / this.columnWidth );
32159 firstCol = Math.max( 0, firstCol );
32161 var lastCol = Math.floor( lastX / this.columnWidth );
32162 // lastCol should not go over if multiple of columnWidth #425
32163 lastCol -= lastX % this.columnWidth ? 0 : 1;
32164 lastCol = Math.min( this.cols - 1, lastCol );
32166 // set colYs to bottom of the stamp
32167 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32170 for ( var i = firstCol; i <= lastCol; i++ ) {
32171 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32176 _getContainerSize : function()
32178 this.maxY = Math.max.apply( Math, this.colYs );
32183 if ( this.isFitWidth ) {
32184 size.width = this._getContainerFitWidth();
32190 _getContainerFitWidth : function()
32192 var unusedCols = 0;
32193 // count unused columns
32196 if ( this.colYs[i] !== 0 ) {
32201 // fit container to columns that have been used
32202 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32205 needsResizeLayout : function()
32207 var previousWidth = this.containerWidth;
32208 this.getContainerWidth();
32209 return previousWidth !== this.containerWidth;
32224 * @class Roo.bootstrap.MasonryBrick
32225 * @extends Roo.bootstrap.Component
32226 * Bootstrap MasonryBrick class
32229 * Create a new MasonryBrick
32230 * @param {Object} config The config object
32233 Roo.bootstrap.MasonryBrick = function(config){
32235 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32237 Roo.bootstrap.MasonryBrick.register(this);
32243 * When a MasonryBrick is clcik
32244 * @param {Roo.bootstrap.MasonryBrick} this
32245 * @param {Roo.EventObject} e
32251 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
32254 * @cfg {String} title
32258 * @cfg {String} html
32262 * @cfg {String} bgimage
32266 * @cfg {String} videourl
32270 * @cfg {String} cls
32274 * @cfg {String} href
32278 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32283 * @cfg {String} placetitle (center|bottom)
32288 * @cfg {Boolean} isFitContainer defalut true
32290 isFitContainer : true,
32293 * @cfg {Boolean} preventDefault defalut false
32295 preventDefault : false,
32298 * @cfg {Boolean} inverse defalut false
32300 maskInverse : false,
32302 getAutoCreate : function()
32304 if(!this.isFitContainer){
32305 return this.getSplitAutoCreate();
32308 var cls = 'masonry-brick masonry-brick-full';
32310 if(this.href.length){
32311 cls += ' masonry-brick-link';
32314 if(this.bgimage.length){
32315 cls += ' masonry-brick-image';
32318 if(this.maskInverse){
32319 cls += ' mask-inverse';
32322 if(!this.html.length && !this.maskInverse && !this.videourl.length){
32323 cls += ' enable-mask';
32327 cls += ' masonry-' + this.size + '-brick';
32330 if(this.placetitle.length){
32332 switch (this.placetitle) {
32334 cls += ' masonry-center-title';
32337 cls += ' masonry-bottom-title';
32344 if(!this.html.length && !this.bgimage.length){
32345 cls += ' masonry-center-title';
32348 if(!this.html.length && this.bgimage.length){
32349 cls += ' masonry-bottom-title';
32354 cls += ' ' + this.cls;
32358 tag: (this.href.length) ? 'a' : 'div',
32363 cls: 'masonry-brick-mask'
32367 cls: 'masonry-brick-paragraph',
32373 if(this.href.length){
32374 cfg.href = this.href;
32377 var cn = cfg.cn[1].cn;
32379 if(this.title.length){
32382 cls: 'masonry-brick-title',
32387 if(this.html.length){
32390 cls: 'masonry-brick-text',
32395 if (!this.title.length && !this.html.length) {
32396 cfg.cn[1].cls += ' hide';
32399 if(this.bgimage.length){
32402 cls: 'masonry-brick-image-view',
32407 if(this.videourl.length){
32408 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32409 // youtube support only?
32412 cls: 'masonry-brick-image-view',
32415 allowfullscreen : true
32423 getSplitAutoCreate : function()
32425 var cls = 'masonry-brick masonry-brick-split';
32427 if(this.href.length){
32428 cls += ' masonry-brick-link';
32431 if(this.bgimage.length){
32432 cls += ' masonry-brick-image';
32436 cls += ' masonry-' + this.size + '-brick';
32439 switch (this.placetitle) {
32441 cls += ' masonry-center-title';
32444 cls += ' masonry-bottom-title';
32447 if(!this.bgimage.length){
32448 cls += ' masonry-center-title';
32451 if(this.bgimage.length){
32452 cls += ' masonry-bottom-title';
32458 cls += ' ' + this.cls;
32462 tag: (this.href.length) ? 'a' : 'div',
32467 cls: 'masonry-brick-split-head',
32471 cls: 'masonry-brick-paragraph',
32478 cls: 'masonry-brick-split-body',
32484 if(this.href.length){
32485 cfg.href = this.href;
32488 if(this.title.length){
32489 cfg.cn[0].cn[0].cn.push({
32491 cls: 'masonry-brick-title',
32496 if(this.html.length){
32497 cfg.cn[1].cn.push({
32499 cls: 'masonry-brick-text',
32504 if(this.bgimage.length){
32505 cfg.cn[0].cn.push({
32507 cls: 'masonry-brick-image-view',
32512 if(this.videourl.length){
32513 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32514 // youtube support only?
32515 cfg.cn[0].cn.cn.push({
32517 cls: 'masonry-brick-image-view',
32520 allowfullscreen : true
32527 initEvents: function()
32529 switch (this.size) {
32562 this.el.on('touchstart', this.onTouchStart, this);
32563 this.el.on('touchmove', this.onTouchMove, this);
32564 this.el.on('touchend', this.onTouchEnd, this);
32565 this.el.on('contextmenu', this.onContextMenu, this);
32567 this.el.on('mouseenter' ,this.enter, this);
32568 this.el.on('mouseleave', this.leave, this);
32569 this.el.on('click', this.onClick, this);
32572 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32573 this.parent().bricks.push(this);
32578 onClick: function(e, el)
32580 var time = this.endTimer - this.startTimer;
32581 // Roo.log(e.preventDefault());
32584 e.preventDefault();
32589 if(!this.preventDefault){
32593 e.preventDefault();
32595 if (this.activcClass != '') {
32596 this.selectBrick();
32599 this.fireEvent('click', this);
32602 enter: function(e, el)
32604 e.preventDefault();
32606 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32610 if(this.bgimage.length && this.html.length){
32611 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32615 leave: function(e, el)
32617 e.preventDefault();
32619 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32623 if(this.bgimage.length && this.html.length){
32624 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32628 onTouchStart: function(e, el)
32630 // e.preventDefault();
32632 this.touchmoved = false;
32634 if(!this.isFitContainer){
32638 if(!this.bgimage.length || !this.html.length){
32642 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32644 this.timer = new Date().getTime();
32648 onTouchMove: function(e, el)
32650 this.touchmoved = true;
32653 onContextMenu : function(e,el)
32655 e.preventDefault();
32656 e.stopPropagation();
32660 onTouchEnd: function(e, el)
32662 // e.preventDefault();
32664 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32671 if(!this.bgimage.length || !this.html.length){
32673 if(this.href.length){
32674 window.location.href = this.href;
32680 if(!this.isFitContainer){
32684 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32686 window.location.href = this.href;
32689 //selection on single brick only
32690 selectBrick : function() {
32692 if (!this.parentId) {
32696 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32697 var index = m.selectedBrick.indexOf(this.id);
32700 m.selectedBrick.splice(index,1);
32701 this.el.removeClass(this.activeClass);
32705 for(var i = 0; i < m.selectedBrick.length; i++) {
32706 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32707 b.el.removeClass(b.activeClass);
32710 m.selectedBrick = [];
32712 m.selectedBrick.push(this.id);
32713 this.el.addClass(this.activeClass);
32719 Roo.apply(Roo.bootstrap.MasonryBrick, {
32722 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32724 * register a Masonry Brick
32725 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32728 register : function(brick)
32730 //this.groups[brick.id] = brick;
32731 this.groups.add(brick.id, brick);
32734 * fetch a masonry brick based on the masonry brick ID
32735 * @param {string} the masonry brick to add
32736 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
32739 get: function(brick_id)
32741 // if (typeof(this.groups[brick_id]) == 'undefined') {
32744 // return this.groups[brick_id] ;
32746 if(this.groups.key(brick_id)) {
32747 return this.groups.key(brick_id);
32765 * @class Roo.bootstrap.Brick
32766 * @extends Roo.bootstrap.Component
32767 * Bootstrap Brick class
32770 * Create a new Brick
32771 * @param {Object} config The config object
32774 Roo.bootstrap.Brick = function(config){
32775 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
32781 * When a Brick is click
32782 * @param {Roo.bootstrap.Brick} this
32783 * @param {Roo.EventObject} e
32789 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
32792 * @cfg {String} title
32796 * @cfg {String} html
32800 * @cfg {String} bgimage
32804 * @cfg {String} cls
32808 * @cfg {String} href
32812 * @cfg {String} video
32816 * @cfg {Boolean} square
32820 getAutoCreate : function()
32822 var cls = 'roo-brick';
32824 if(this.href.length){
32825 cls += ' roo-brick-link';
32828 if(this.bgimage.length){
32829 cls += ' roo-brick-image';
32832 if(!this.html.length && !this.bgimage.length){
32833 cls += ' roo-brick-center-title';
32836 if(!this.html.length && this.bgimage.length){
32837 cls += ' roo-brick-bottom-title';
32841 cls += ' ' + this.cls;
32845 tag: (this.href.length) ? 'a' : 'div',
32850 cls: 'roo-brick-paragraph',
32856 if(this.href.length){
32857 cfg.href = this.href;
32860 var cn = cfg.cn[0].cn;
32862 if(this.title.length){
32865 cls: 'roo-brick-title',
32870 if(this.html.length){
32873 cls: 'roo-brick-text',
32880 if(this.bgimage.length){
32883 cls: 'roo-brick-image-view',
32891 initEvents: function()
32893 if(this.title.length || this.html.length){
32894 this.el.on('mouseenter' ,this.enter, this);
32895 this.el.on('mouseleave', this.leave, this);
32898 Roo.EventManager.onWindowResize(this.resize, this);
32900 if(this.bgimage.length){
32901 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
32902 this.imageEl.on('load', this.onImageLoad, this);
32909 onImageLoad : function()
32914 resize : function()
32916 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
32918 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
32920 if(this.bgimage.length){
32921 var image = this.el.select('.roo-brick-image-view', true).first();
32923 image.setWidth(paragraph.getWidth());
32926 image.setHeight(paragraph.getWidth());
32929 this.el.setHeight(image.getHeight());
32930 paragraph.setHeight(image.getHeight());
32936 enter: function(e, el)
32938 e.preventDefault();
32940 if(this.bgimage.length){
32941 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
32942 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
32946 leave: function(e, el)
32948 e.preventDefault();
32950 if(this.bgimage.length){
32951 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
32952 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
32968 * @class Roo.bootstrap.NumberField
32969 * @extends Roo.bootstrap.Input
32970 * Bootstrap NumberField class
32976 * Create a new NumberField
32977 * @param {Object} config The config object
32980 Roo.bootstrap.NumberField = function(config){
32981 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
32984 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
32987 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
32989 allowDecimals : true,
32991 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
32993 decimalSeparator : ".",
32995 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
32997 decimalPrecision : 2,
32999 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33001 allowNegative : true,
33003 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33005 minValue : Number.NEGATIVE_INFINITY,
33007 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33009 maxValue : Number.MAX_VALUE,
33011 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33013 minText : "The minimum value for this field is {0}",
33015 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33017 maxText : "The maximum value for this field is {0}",
33019 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
33020 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33022 nanText : "{0} is not a valid number",
33024 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
33029 initEvents : function()
33031 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33033 var allowed = "0123456789";
33035 if(this.allowDecimals){
33036 allowed += this.decimalSeparator;
33039 if(this.allowNegative){
33043 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33045 var keyPress = function(e){
33047 var k = e.getKey();
33049 var c = e.getCharCode();
33052 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33053 allowed.indexOf(String.fromCharCode(c)) === -1
33059 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33063 if(allowed.indexOf(String.fromCharCode(c)) === -1){
33068 this.el.on("keypress", keyPress, this);
33071 validateValue : function(value)
33074 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33078 var num = this.parseValue(value);
33081 this.markInvalid(String.format(this.nanText, value));
33085 if(num < this.minValue){
33086 this.markInvalid(String.format(this.minText, this.minValue));
33090 if(num > this.maxValue){
33091 this.markInvalid(String.format(this.maxText, this.maxValue));
33098 getValue : function()
33100 return this.fixPrecision(this.parseValue(Roo.bootstrap.NumberField.superclass.getValue.call(this)));
33103 parseValue : function(value)
33105 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33106 return isNaN(value) ? '' : value;
33109 fixPrecision : function(value)
33111 var nan = isNaN(value);
33113 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33114 return nan ? '' : value;
33116 return parseFloat(value).toFixed(this.decimalPrecision);
33119 setValue : function(v)
33121 v = this.fixPrecision(v);
33122 Roo.bootstrap.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
33125 decimalPrecisionFcn : function(v)
33127 return Math.floor(v);
33130 beforeBlur : function()
33136 var v = this.parseValue(this.getRawValue());
33151 * @class Roo.bootstrap.DocumentSlider
33152 * @extends Roo.bootstrap.Component
33153 * Bootstrap DocumentSlider class
33156 * Create a new DocumentViewer
33157 * @param {Object} config The config object
33160 Roo.bootstrap.DocumentSlider = function(config){
33161 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33168 * Fire after initEvent
33169 * @param {Roo.bootstrap.DocumentSlider} this
33174 * Fire after update
33175 * @param {Roo.bootstrap.DocumentSlider} this
33181 * @param {Roo.bootstrap.DocumentSlider} this
33187 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
33193 getAutoCreate : function()
33197 cls : 'roo-document-slider',
33201 cls : 'roo-document-slider-header',
33205 cls : 'roo-document-slider-header-title'
33211 cls : 'roo-document-slider-body',
33215 cls : 'roo-document-slider-prev',
33219 cls : 'fa fa-chevron-left'
33225 cls : 'roo-document-slider-thumb',
33229 cls : 'roo-document-slider-image'
33235 cls : 'roo-document-slider-next',
33239 cls : 'fa fa-chevron-right'
33251 initEvents : function()
33253 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33254 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33256 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33257 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33259 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33260 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33262 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33263 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33265 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33266 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33268 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33269 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33271 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33272 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33274 this.thumbEl.on('click', this.onClick, this);
33276 this.prevIndicator.on('click', this.prev, this);
33278 this.nextIndicator.on('click', this.next, this);
33282 initial : function()
33284 if(this.files.length){
33285 this.indicator = 1;
33289 this.fireEvent('initial', this);
33292 update : function()
33294 this.imageEl.attr('src', this.files[this.indicator - 1]);
33296 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33298 this.prevIndicator.show();
33300 if(this.indicator == 1){
33301 this.prevIndicator.hide();
33304 this.nextIndicator.show();
33306 if(this.indicator == this.files.length){
33307 this.nextIndicator.hide();
33310 this.thumbEl.scrollTo('top');
33312 this.fireEvent('update', this);
33315 onClick : function(e)
33317 e.preventDefault();
33319 this.fireEvent('click', this);
33324 e.preventDefault();
33326 this.indicator = Math.max(1, this.indicator - 1);
33333 e.preventDefault();
33335 this.indicator = Math.min(this.files.length, this.indicator + 1);
33349 * @class Roo.bootstrap.RadioSet
33350 * @extends Roo.bootstrap.Input
33351 * Bootstrap RadioSet class
33352 * @cfg {String} indicatorpos (left|right) default left
33353 * @cfg {Boolean} inline (true|false) inline the element (default true)
33354 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33356 * Create a new RadioSet
33357 * @param {Object} config The config object
33360 Roo.bootstrap.RadioSet = function(config){
33362 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33366 Roo.bootstrap.RadioSet.register(this);
33371 * Fires when the element is checked or unchecked.
33372 * @param {Roo.bootstrap.RadioSet} this This radio
33373 * @param {Roo.bootstrap.Radio} item The checked item
33380 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
33388 indicatorpos : 'left',
33390 getAutoCreate : function()
33394 cls : 'roo-radio-set-label',
33398 html : this.fieldLabel
33403 if(this.indicatorpos == 'left'){
33406 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33407 tooltip : 'This field is required'
33412 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33413 tooltip : 'This field is required'
33419 cls : 'roo-radio-set-items'
33422 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33424 if (align === 'left' && this.fieldLabel.length) {
33427 cls : "roo-radio-set-right",
33433 if(this.labelWidth > 12){
33434 label.style = "width: " + this.labelWidth + 'px';
33437 if(this.labelWidth < 13 && this.labelmd == 0){
33438 this.labelmd = this.labelWidth;
33441 if(this.labellg > 0){
33442 label.cls += ' col-lg-' + this.labellg;
33443 items.cls += ' col-lg-' + (12 - this.labellg);
33446 if(this.labelmd > 0){
33447 label.cls += ' col-md-' + this.labelmd;
33448 items.cls += ' col-md-' + (12 - this.labelmd);
33451 if(this.labelsm > 0){
33452 label.cls += ' col-sm-' + this.labelsm;
33453 items.cls += ' col-sm-' + (12 - this.labelsm);
33456 if(this.labelxs > 0){
33457 label.cls += ' col-xs-' + this.labelxs;
33458 items.cls += ' col-xs-' + (12 - this.labelxs);
33464 cls : 'roo-radio-set',
33468 cls : 'roo-radio-set-input',
33471 value : this.value ? this.value : ''
33478 if(this.weight.length){
33479 cfg.cls += ' roo-radio-' + this.weight;
33483 cfg.cls += ' roo-radio-set-inline';
33487 ['xs','sm','md','lg'].map(function(size){
33488 if (settings[size]) {
33489 cfg.cls += ' col-' + size + '-' + settings[size];
33497 initEvents : function()
33499 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33500 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33502 if(!this.fieldLabel.length){
33503 this.labelEl.hide();
33506 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33507 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33509 this.indicatorEl().addClass('invisible');
33511 this.originalValue = this.getValue();
33515 inputEl: function ()
33517 return this.el.select('.roo-radio-set-input', true).first();
33520 getChildContainer : function()
33522 return this.itemsEl;
33525 register : function(item)
33527 this.radioes.push(item);
33531 validate : function()
33535 Roo.each(this.radioes, function(i){
33544 if(this.allowBlank) {
33548 if(this.disabled || valid){
33553 this.markInvalid();
33558 markValid : function()
33560 if(this.labelEl.isVisible(true)){
33561 this.indicatorEl().removeClass('visible');
33562 this.indicatorEl().addClass('invisible');
33565 this.el.removeClass([this.invalidClass, this.validClass]);
33566 this.el.addClass(this.validClass);
33568 this.fireEvent('valid', this);
33571 markInvalid : function(msg)
33573 if(this.allowBlank || this.disabled){
33577 if(this.labelEl.isVisible(true)){
33578 this.indicatorEl().removeClass('invisible');
33579 this.indicatorEl().addClass('visible');
33582 this.el.removeClass([this.invalidClass, this.validClass]);
33583 this.el.addClass(this.invalidClass);
33585 this.fireEvent('invalid', this, msg);
33589 setValue : function(v, suppressEvent)
33591 if(this.value === v){
33598 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33601 Roo.each(this.radioes, function(i){
33604 i.el.removeClass('checked');
33606 if(i.value === v || i.value.toString() === v.toString()){
33608 i.el.addClass('checked');
33610 if(suppressEvent !== true){
33611 this.fireEvent('check', this, i);
33620 clearInvalid : function(){
33622 if(!this.el || this.preventMark){
33626 this.el.removeClass([this.invalidClass]);
33628 this.fireEvent('valid', this);
33633 Roo.apply(Roo.bootstrap.RadioSet, {
33637 register : function(set)
33639 this.groups[set.name] = set;
33642 get: function(name)
33644 if (typeof(this.groups[name]) == 'undefined') {
33648 return this.groups[name] ;
33654 * Ext JS Library 1.1.1
33655 * Copyright(c) 2006-2007, Ext JS, LLC.
33657 * Originally Released Under LGPL - original licence link has changed is not relivant.
33660 * <script type="text/javascript">
33665 * @class Roo.bootstrap.SplitBar
33666 * @extends Roo.util.Observable
33667 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
33671 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
33672 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
33673 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
33674 split.minSize = 100;
33675 split.maxSize = 600;
33676 split.animate = true;
33677 split.on('moved', splitterMoved);
33680 * Create a new SplitBar
33681 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
33682 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
33683 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33684 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
33685 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
33686 position of the SplitBar).
33688 Roo.bootstrap.SplitBar = function(cfg){
33693 // dragElement : elm
33694 // resizingElement: el,
33696 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
33697 // placement : Roo.bootstrap.SplitBar.LEFT ,
33698 // existingProxy ???
33701 this.el = Roo.get(cfg.dragElement, true);
33702 this.el.dom.unselectable = "on";
33704 this.resizingEl = Roo.get(cfg.resizingElement, true);
33708 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33709 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
33712 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
33715 * The minimum size of the resizing element. (Defaults to 0)
33721 * The maximum size of the resizing element. (Defaults to 2000)
33724 this.maxSize = 2000;
33727 * Whether to animate the transition to the new size
33730 this.animate = false;
33733 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
33736 this.useShim = false;
33741 if(!cfg.existingProxy){
33743 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
33745 this.proxy = Roo.get(cfg.existingProxy).dom;
33748 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
33751 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
33754 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
33757 this.dragSpecs = {};
33760 * @private The adapter to use to positon and resize elements
33762 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33763 this.adapter.init(this);
33765 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33767 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
33768 this.el.addClass("roo-splitbar-h");
33771 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
33772 this.el.addClass("roo-splitbar-v");
33778 * Fires when the splitter is moved (alias for {@link #event-moved})
33779 * @param {Roo.bootstrap.SplitBar} this
33780 * @param {Number} newSize the new width or height
33785 * Fires when the splitter is moved
33786 * @param {Roo.bootstrap.SplitBar} this
33787 * @param {Number} newSize the new width or height
33791 * @event beforeresize
33792 * Fires before the splitter is dragged
33793 * @param {Roo.bootstrap.SplitBar} this
33795 "beforeresize" : true,
33797 "beforeapply" : true
33800 Roo.util.Observable.call(this);
33803 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
33804 onStartProxyDrag : function(x, y){
33805 this.fireEvent("beforeresize", this);
33807 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
33809 o.enableDisplayMode("block");
33810 // all splitbars share the same overlay
33811 Roo.bootstrap.SplitBar.prototype.overlay = o;
33813 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33814 this.overlay.show();
33815 Roo.get(this.proxy).setDisplayed("block");
33816 var size = this.adapter.getElementSize(this);
33817 this.activeMinSize = this.getMinimumSize();;
33818 this.activeMaxSize = this.getMaximumSize();;
33819 var c1 = size - this.activeMinSize;
33820 var c2 = Math.max(this.activeMaxSize - size, 0);
33821 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33822 this.dd.resetConstraints();
33823 this.dd.setXConstraint(
33824 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
33825 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
33827 this.dd.setYConstraint(0, 0);
33829 this.dd.resetConstraints();
33830 this.dd.setXConstraint(0, 0);
33831 this.dd.setYConstraint(
33832 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
33833 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
33836 this.dragSpecs.startSize = size;
33837 this.dragSpecs.startPoint = [x, y];
33838 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
33842 * @private Called after the drag operation by the DDProxy
33844 onEndProxyDrag : function(e){
33845 Roo.get(this.proxy).setDisplayed(false);
33846 var endPoint = Roo.lib.Event.getXY(e);
33848 this.overlay.hide();
33851 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33852 newSize = this.dragSpecs.startSize +
33853 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
33854 endPoint[0] - this.dragSpecs.startPoint[0] :
33855 this.dragSpecs.startPoint[0] - endPoint[0]
33858 newSize = this.dragSpecs.startSize +
33859 (this.placement == Roo.bootstrap.SplitBar.TOP ?
33860 endPoint[1] - this.dragSpecs.startPoint[1] :
33861 this.dragSpecs.startPoint[1] - endPoint[1]
33864 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
33865 if(newSize != this.dragSpecs.startSize){
33866 if(this.fireEvent('beforeapply', this, newSize) !== false){
33867 this.adapter.setElementSize(this, newSize);
33868 this.fireEvent("moved", this, newSize);
33869 this.fireEvent("resize", this, newSize);
33875 * Get the adapter this SplitBar uses
33876 * @return The adapter object
33878 getAdapter : function(){
33879 return this.adapter;
33883 * Set the adapter this SplitBar uses
33884 * @param {Object} adapter A SplitBar adapter object
33886 setAdapter : function(adapter){
33887 this.adapter = adapter;
33888 this.adapter.init(this);
33892 * Gets the minimum size for the resizing element
33893 * @return {Number} The minimum size
33895 getMinimumSize : function(){
33896 return this.minSize;
33900 * Sets the minimum size for the resizing element
33901 * @param {Number} minSize The minimum size
33903 setMinimumSize : function(minSize){
33904 this.minSize = minSize;
33908 * Gets the maximum size for the resizing element
33909 * @return {Number} The maximum size
33911 getMaximumSize : function(){
33912 return this.maxSize;
33916 * Sets the maximum size for the resizing element
33917 * @param {Number} maxSize The maximum size
33919 setMaximumSize : function(maxSize){
33920 this.maxSize = maxSize;
33924 * Sets the initialize size for the resizing element
33925 * @param {Number} size The initial size
33927 setCurrentSize : function(size){
33928 var oldAnimate = this.animate;
33929 this.animate = false;
33930 this.adapter.setElementSize(this, size);
33931 this.animate = oldAnimate;
33935 * Destroy this splitbar.
33936 * @param {Boolean} removeEl True to remove the element
33938 destroy : function(removeEl){
33940 this.shim.remove();
33943 this.proxy.parentNode.removeChild(this.proxy);
33951 * @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.
33953 Roo.bootstrap.SplitBar.createProxy = function(dir){
33954 var proxy = new Roo.Element(document.createElement("div"));
33955 proxy.unselectable();
33956 var cls = 'roo-splitbar-proxy';
33957 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
33958 document.body.appendChild(proxy.dom);
33963 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
33964 * Default Adapter. It assumes the splitter and resizing element are not positioned
33965 * elements and only gets/sets the width of the element. Generally used for table based layouts.
33967 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
33970 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
33971 // do nothing for now
33972 init : function(s){
33976 * Called before drag operations to get the current size of the resizing element.
33977 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33979 getElementSize : function(s){
33980 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33981 return s.resizingEl.getWidth();
33983 return s.resizingEl.getHeight();
33988 * Called after drag operations to set the size of the resizing element.
33989 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33990 * @param {Number} newSize The new size to set
33991 * @param {Function} onComplete A function to be invoked when resizing is complete
33993 setElementSize : function(s, newSize, onComplete){
33994 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33996 s.resizingEl.setWidth(newSize);
33998 onComplete(s, newSize);
34001 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34006 s.resizingEl.setHeight(newSize);
34008 onComplete(s, newSize);
34011 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34018 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34019 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34020 * Adapter that moves the splitter element to align with the resized sizing element.
34021 * Used with an absolute positioned SplitBar.
34022 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34023 * document.body, make sure you assign an id to the body element.
34025 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34026 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34027 this.container = Roo.get(container);
34030 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34031 init : function(s){
34032 this.basic.init(s);
34035 getElementSize : function(s){
34036 return this.basic.getElementSize(s);
34039 setElementSize : function(s, newSize, onComplete){
34040 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34043 moveSplitter : function(s){
34044 var yes = Roo.bootstrap.SplitBar;
34045 switch(s.placement){
34047 s.el.setX(s.resizingEl.getRight());
34050 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34053 s.el.setY(s.resizingEl.getBottom());
34056 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34063 * Orientation constant - Create a vertical SplitBar
34067 Roo.bootstrap.SplitBar.VERTICAL = 1;
34070 * Orientation constant - Create a horizontal SplitBar
34074 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34077 * Placement constant - The resizing element is to the left of the splitter element
34081 Roo.bootstrap.SplitBar.LEFT = 1;
34084 * Placement constant - The resizing element is to the right of the splitter element
34088 Roo.bootstrap.SplitBar.RIGHT = 2;
34091 * Placement constant - The resizing element is positioned above the splitter element
34095 Roo.bootstrap.SplitBar.TOP = 3;
34098 * Placement constant - The resizing element is positioned under splitter element
34102 Roo.bootstrap.SplitBar.BOTTOM = 4;
34103 Roo.namespace("Roo.bootstrap.layout");/*
34105 * Ext JS Library 1.1.1
34106 * Copyright(c) 2006-2007, Ext JS, LLC.
34108 * Originally Released Under LGPL - original licence link has changed is not relivant.
34111 * <script type="text/javascript">
34115 * @class Roo.bootstrap.layout.Manager
34116 * @extends Roo.bootstrap.Component
34117 * Base class for layout managers.
34119 Roo.bootstrap.layout.Manager = function(config)
34121 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34127 /** false to disable window resize monitoring @type Boolean */
34128 this.monitorWindowResize = true;
34133 * Fires when a layout is performed.
34134 * @param {Roo.LayoutManager} this
34138 * @event regionresized
34139 * Fires when the user resizes a region.
34140 * @param {Roo.LayoutRegion} region The resized region
34141 * @param {Number} newSize The new size (width for east/west, height for north/south)
34143 "regionresized" : true,
34145 * @event regioncollapsed
34146 * Fires when a region is collapsed.
34147 * @param {Roo.LayoutRegion} region The collapsed region
34149 "regioncollapsed" : true,
34151 * @event regionexpanded
34152 * Fires when a region is expanded.
34153 * @param {Roo.LayoutRegion} region The expanded region
34155 "regionexpanded" : true
34157 this.updating = false;
34160 this.el = Roo.get(config.el);
34166 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34171 monitorWindowResize : true,
34177 onRender : function(ct, position)
34180 this.el = Roo.get(ct);
34183 //this.fireEvent('render',this);
34187 initEvents: function()
34191 // ie scrollbar fix
34192 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34193 document.body.scroll = "no";
34194 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34195 this.el.position('relative');
34197 this.id = this.el.id;
34198 this.el.addClass("roo-layout-container");
34199 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34200 if(this.el.dom != document.body ) {
34201 this.el.on('resize', this.layout,this);
34202 this.el.on('show', this.layout,this);
34208 * Returns true if this layout is currently being updated
34209 * @return {Boolean}
34211 isUpdating : function(){
34212 return this.updating;
34216 * Suspend the LayoutManager from doing auto-layouts while
34217 * making multiple add or remove calls
34219 beginUpdate : function(){
34220 this.updating = true;
34224 * Restore auto-layouts and optionally disable the manager from performing a layout
34225 * @param {Boolean} noLayout true to disable a layout update
34227 endUpdate : function(noLayout){
34228 this.updating = false;
34234 layout: function(){
34238 onRegionResized : function(region, newSize){
34239 this.fireEvent("regionresized", region, newSize);
34243 onRegionCollapsed : function(region){
34244 this.fireEvent("regioncollapsed", region);
34247 onRegionExpanded : function(region){
34248 this.fireEvent("regionexpanded", region);
34252 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34253 * performs box-model adjustments.
34254 * @return {Object} The size as an object {width: (the width), height: (the height)}
34256 getViewSize : function()
34259 if(this.el.dom != document.body){
34260 size = this.el.getSize();
34262 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34264 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34265 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34270 * Returns the Element this layout is bound to.
34271 * @return {Roo.Element}
34273 getEl : function(){
34278 * Returns the specified region.
34279 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34280 * @return {Roo.LayoutRegion}
34282 getRegion : function(target){
34283 return this.regions[target.toLowerCase()];
34286 onWindowResize : function(){
34287 if(this.monitorWindowResize){
34294 * Ext JS Library 1.1.1
34295 * Copyright(c) 2006-2007, Ext JS, LLC.
34297 * Originally Released Under LGPL - original licence link has changed is not relivant.
34300 * <script type="text/javascript">
34303 * @class Roo.bootstrap.layout.Border
34304 * @extends Roo.bootstrap.layout.Manager
34305 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34306 * please see: examples/bootstrap/nested.html<br><br>
34308 <b>The container the layout is rendered into can be either the body element or any other element.
34309 If it is not the body element, the container needs to either be an absolute positioned element,
34310 or you will need to add "position:relative" to the css of the container. You will also need to specify
34311 the container size if it is not the body element.</b>
34314 * Create a new Border
34315 * @param {Object} config Configuration options
34317 Roo.bootstrap.layout.Border = function(config){
34318 config = config || {};
34319 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34323 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34324 if(config[region]){
34325 config[region].region = region;
34326 this.addRegion(config[region]);
34332 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
34334 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34336 * Creates and adds a new region if it doesn't already exist.
34337 * @param {String} target The target region key (north, south, east, west or center).
34338 * @param {Object} config The regions config object
34339 * @return {BorderLayoutRegion} The new region
34341 addRegion : function(config)
34343 if(!this.regions[config.region]){
34344 var r = this.factory(config);
34345 this.bindRegion(r);
34347 return this.regions[config.region];
34351 bindRegion : function(r){
34352 this.regions[r.config.region] = r;
34354 r.on("visibilitychange", this.layout, this);
34355 r.on("paneladded", this.layout, this);
34356 r.on("panelremoved", this.layout, this);
34357 r.on("invalidated", this.layout, this);
34358 r.on("resized", this.onRegionResized, this);
34359 r.on("collapsed", this.onRegionCollapsed, this);
34360 r.on("expanded", this.onRegionExpanded, this);
34364 * Performs a layout update.
34366 layout : function()
34368 if(this.updating) {
34372 // render all the rebions if they have not been done alreayd?
34373 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34374 if(this.regions[region] && !this.regions[region].bodyEl){
34375 this.regions[region].onRender(this.el)
34379 var size = this.getViewSize();
34380 var w = size.width;
34381 var h = size.height;
34386 //var x = 0, y = 0;
34388 var rs = this.regions;
34389 var north = rs["north"];
34390 var south = rs["south"];
34391 var west = rs["west"];
34392 var east = rs["east"];
34393 var center = rs["center"];
34394 //if(this.hideOnLayout){ // not supported anymore
34395 //c.el.setStyle("display", "none");
34397 if(north && north.isVisible()){
34398 var b = north.getBox();
34399 var m = north.getMargins();
34400 b.width = w - (m.left+m.right);
34403 centerY = b.height + b.y + m.bottom;
34404 centerH -= centerY;
34405 north.updateBox(this.safeBox(b));
34407 if(south && south.isVisible()){
34408 var b = south.getBox();
34409 var m = south.getMargins();
34410 b.width = w - (m.left+m.right);
34412 var totalHeight = (b.height + m.top + m.bottom);
34413 b.y = h - totalHeight + m.top;
34414 centerH -= totalHeight;
34415 south.updateBox(this.safeBox(b));
34417 if(west && west.isVisible()){
34418 var b = west.getBox();
34419 var m = west.getMargins();
34420 b.height = centerH - (m.top+m.bottom);
34422 b.y = centerY + m.top;
34423 var totalWidth = (b.width + m.left + m.right);
34424 centerX += totalWidth;
34425 centerW -= totalWidth;
34426 west.updateBox(this.safeBox(b));
34428 if(east && east.isVisible()){
34429 var b = east.getBox();
34430 var m = east.getMargins();
34431 b.height = centerH - (m.top+m.bottom);
34432 var totalWidth = (b.width + m.left + m.right);
34433 b.x = w - totalWidth + m.left;
34434 b.y = centerY + m.top;
34435 centerW -= totalWidth;
34436 east.updateBox(this.safeBox(b));
34439 var m = center.getMargins();
34441 x: centerX + m.left,
34442 y: centerY + m.top,
34443 width: centerW - (m.left+m.right),
34444 height: centerH - (m.top+m.bottom)
34446 //if(this.hideOnLayout){
34447 //center.el.setStyle("display", "block");
34449 center.updateBox(this.safeBox(centerBox));
34452 this.fireEvent("layout", this);
34456 safeBox : function(box){
34457 box.width = Math.max(0, box.width);
34458 box.height = Math.max(0, box.height);
34463 * Adds a ContentPanel (or subclass) to this layout.
34464 * @param {String} target The target region key (north, south, east, west or center).
34465 * @param {Roo.ContentPanel} panel The panel to add
34466 * @return {Roo.ContentPanel} The added panel
34468 add : function(target, panel){
34470 target = target.toLowerCase();
34471 return this.regions[target].add(panel);
34475 * Remove a ContentPanel (or subclass) to this layout.
34476 * @param {String} target The target region key (north, south, east, west or center).
34477 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34478 * @return {Roo.ContentPanel} The removed panel
34480 remove : function(target, panel){
34481 target = target.toLowerCase();
34482 return this.regions[target].remove(panel);
34486 * Searches all regions for a panel with the specified id
34487 * @param {String} panelId
34488 * @return {Roo.ContentPanel} The panel or null if it wasn't found
34490 findPanel : function(panelId){
34491 var rs = this.regions;
34492 for(var target in rs){
34493 if(typeof rs[target] != "function"){
34494 var p = rs[target].getPanel(panelId);
34504 * Searches all regions for a panel with the specified id and activates (shows) it.
34505 * @param {String/ContentPanel} panelId The panels id or the panel itself
34506 * @return {Roo.ContentPanel} The shown panel or null
34508 showPanel : function(panelId) {
34509 var rs = this.regions;
34510 for(var target in rs){
34511 var r = rs[target];
34512 if(typeof r != "function"){
34513 if(r.hasPanel(panelId)){
34514 return r.showPanel(panelId);
34522 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34523 * @param {Roo.state.Provider} provider (optional) An alternate state provider
34526 restoreState : function(provider){
34528 provider = Roo.state.Manager;
34530 var sm = new Roo.LayoutStateManager();
34531 sm.init(this, provider);
34537 * Adds a xtype elements to the layout.
34541 xtype : 'ContentPanel',
34548 xtype : 'NestedLayoutPanel',
34554 items : [ ... list of content panels or nested layout panels.. ]
34558 * @param {Object} cfg Xtype definition of item to add.
34560 addxtype : function(cfg)
34562 // basically accepts a pannel...
34563 // can accept a layout region..!?!?
34564 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34567 // theory? children can only be panels??
34569 //if (!cfg.xtype.match(/Panel$/)) {
34574 if (typeof(cfg.region) == 'undefined') {
34575 Roo.log("Failed to add Panel, region was not set");
34579 var region = cfg.region;
34585 xitems = cfg.items;
34592 case 'Content': // ContentPanel (el, cfg)
34593 case 'Scroll': // ContentPanel (el, cfg)
34595 cfg.autoCreate = true;
34596 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34598 // var el = this.el.createChild();
34599 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34602 this.add(region, ret);
34606 case 'TreePanel': // our new panel!
34607 cfg.el = this.el.createChild();
34608 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34609 this.add(region, ret);
34614 // create a new Layout (which is a Border Layout...
34616 var clayout = cfg.layout;
34617 clayout.el = this.el.createChild();
34618 clayout.items = clayout.items || [];
34622 // replace this exitems with the clayout ones..
34623 xitems = clayout.items;
34625 // force background off if it's in center...
34626 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34627 cfg.background = false;
34629 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
34632 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34633 //console.log('adding nested layout panel ' + cfg.toSource());
34634 this.add(region, ret);
34635 nb = {}; /// find first...
34640 // needs grid and region
34642 //var el = this.getRegion(region).el.createChild();
34644 *var el = this.el.createChild();
34645 // create the grid first...
34646 cfg.grid.container = el;
34647 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
34650 if (region == 'center' && this.active ) {
34651 cfg.background = false;
34654 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34656 this.add(region, ret);
34658 if (cfg.background) {
34659 // render grid on panel activation (if panel background)
34660 ret.on('activate', function(gp) {
34661 if (!gp.grid.rendered) {
34662 // gp.grid.render(el);
34666 // cfg.grid.render(el);
34672 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
34673 // it was the old xcomponent building that caused this before.
34674 // espeically if border is the top element in the tree.
34684 if (typeof(Roo[cfg.xtype]) != 'undefined') {
34686 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34687 this.add(region, ret);
34691 throw "Can not add '" + cfg.xtype + "' to Border";
34697 this.beginUpdate();
34701 Roo.each(xitems, function(i) {
34702 region = nb && i.region ? i.region : false;
34704 var add = ret.addxtype(i);
34707 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
34708 if (!i.background) {
34709 abn[region] = nb[region] ;
34716 // make the last non-background panel active..
34717 //if (nb) { Roo.log(abn); }
34720 for(var r in abn) {
34721 region = this.getRegion(r);
34723 // tried using nb[r], but it does not work..
34725 region.showPanel(abn[r]);
34736 factory : function(cfg)
34739 var validRegions = Roo.bootstrap.layout.Border.regions;
34741 var target = cfg.region;
34744 var r = Roo.bootstrap.layout;
34748 return new r.North(cfg);
34750 return new r.South(cfg);
34752 return new r.East(cfg);
34754 return new r.West(cfg);
34756 return new r.Center(cfg);
34758 throw 'Layout region "'+target+'" not supported.';
34765 * Ext JS Library 1.1.1
34766 * Copyright(c) 2006-2007, Ext JS, LLC.
34768 * Originally Released Under LGPL - original licence link has changed is not relivant.
34771 * <script type="text/javascript">
34775 * @class Roo.bootstrap.layout.Basic
34776 * @extends Roo.util.Observable
34777 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
34778 * and does not have a titlebar, tabs or any other features. All it does is size and position
34779 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
34780 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
34781 * @cfg {string} region the region that it inhabits..
34782 * @cfg {bool} skipConfig skip config?
34786 Roo.bootstrap.layout.Basic = function(config){
34788 this.mgr = config.mgr;
34790 this.position = config.region;
34792 var skipConfig = config.skipConfig;
34796 * @scope Roo.BasicLayoutRegion
34800 * @event beforeremove
34801 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
34802 * @param {Roo.LayoutRegion} this
34803 * @param {Roo.ContentPanel} panel The panel
34804 * @param {Object} e The cancel event object
34806 "beforeremove" : true,
34808 * @event invalidated
34809 * Fires when the layout for this region is changed.
34810 * @param {Roo.LayoutRegion} this
34812 "invalidated" : true,
34814 * @event visibilitychange
34815 * Fires when this region is shown or hidden
34816 * @param {Roo.LayoutRegion} this
34817 * @param {Boolean} visibility true or false
34819 "visibilitychange" : true,
34821 * @event paneladded
34822 * Fires when a panel is added.
34823 * @param {Roo.LayoutRegion} this
34824 * @param {Roo.ContentPanel} panel The panel
34826 "paneladded" : true,
34828 * @event panelremoved
34829 * Fires when a panel is removed.
34830 * @param {Roo.LayoutRegion} this
34831 * @param {Roo.ContentPanel} panel The panel
34833 "panelremoved" : true,
34835 * @event beforecollapse
34836 * Fires when this region before collapse.
34837 * @param {Roo.LayoutRegion} this
34839 "beforecollapse" : true,
34842 * Fires when this region is collapsed.
34843 * @param {Roo.LayoutRegion} this
34845 "collapsed" : true,
34848 * Fires when this region is expanded.
34849 * @param {Roo.LayoutRegion} this
34854 * Fires when this region is slid into view.
34855 * @param {Roo.LayoutRegion} this
34857 "slideshow" : true,
34860 * Fires when this region slides out of view.
34861 * @param {Roo.LayoutRegion} this
34863 "slidehide" : true,
34865 * @event panelactivated
34866 * Fires when a panel is activated.
34867 * @param {Roo.LayoutRegion} this
34868 * @param {Roo.ContentPanel} panel The activated panel
34870 "panelactivated" : true,
34873 * Fires when the user resizes this region.
34874 * @param {Roo.LayoutRegion} this
34875 * @param {Number} newSize The new size (width for east/west, height for north/south)
34879 /** A collection of panels in this region. @type Roo.util.MixedCollection */
34880 this.panels = new Roo.util.MixedCollection();
34881 this.panels.getKey = this.getPanelId.createDelegate(this);
34883 this.activePanel = null;
34884 // ensure listeners are added...
34886 if (config.listeners || config.events) {
34887 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
34888 listeners : config.listeners || {},
34889 events : config.events || {}
34893 if(skipConfig !== true){
34894 this.applyConfig(config);
34898 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
34900 getPanelId : function(p){
34904 applyConfig : function(config){
34905 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
34906 this.config = config;
34911 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
34912 * the width, for horizontal (north, south) the height.
34913 * @param {Number} newSize The new width or height
34915 resizeTo : function(newSize){
34916 var el = this.el ? this.el :
34917 (this.activePanel ? this.activePanel.getEl() : null);
34919 switch(this.position){
34922 el.setWidth(newSize);
34923 this.fireEvent("resized", this, newSize);
34927 el.setHeight(newSize);
34928 this.fireEvent("resized", this, newSize);
34934 getBox : function(){
34935 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
34938 getMargins : function(){
34939 return this.margins;
34942 updateBox : function(box){
34944 var el = this.activePanel.getEl();
34945 el.dom.style.left = box.x + "px";
34946 el.dom.style.top = box.y + "px";
34947 this.activePanel.setSize(box.width, box.height);
34951 * Returns the container element for this region.
34952 * @return {Roo.Element}
34954 getEl : function(){
34955 return this.activePanel;
34959 * Returns true if this region is currently visible.
34960 * @return {Boolean}
34962 isVisible : function(){
34963 return this.activePanel ? true : false;
34966 setActivePanel : function(panel){
34967 panel = this.getPanel(panel);
34968 if(this.activePanel && this.activePanel != panel){
34969 this.activePanel.setActiveState(false);
34970 this.activePanel.getEl().setLeftTop(-10000,-10000);
34972 this.activePanel = panel;
34973 panel.setActiveState(true);
34975 panel.setSize(this.box.width, this.box.height);
34977 this.fireEvent("panelactivated", this, panel);
34978 this.fireEvent("invalidated");
34982 * Show the specified panel.
34983 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
34984 * @return {Roo.ContentPanel} The shown panel or null
34986 showPanel : function(panel){
34987 panel = this.getPanel(panel);
34989 this.setActivePanel(panel);
34995 * Get the active panel for this region.
34996 * @return {Roo.ContentPanel} The active panel or null
34998 getActivePanel : function(){
34999 return this.activePanel;
35003 * Add the passed ContentPanel(s)
35004 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35005 * @return {Roo.ContentPanel} The panel added (if only one was added)
35007 add : function(panel){
35008 if(arguments.length > 1){
35009 for(var i = 0, len = arguments.length; i < len; i++) {
35010 this.add(arguments[i]);
35014 if(this.hasPanel(panel)){
35015 this.showPanel(panel);
35018 var el = panel.getEl();
35019 if(el.dom.parentNode != this.mgr.el.dom){
35020 this.mgr.el.dom.appendChild(el.dom);
35022 if(panel.setRegion){
35023 panel.setRegion(this);
35025 this.panels.add(panel);
35026 el.setStyle("position", "absolute");
35027 if(!panel.background){
35028 this.setActivePanel(panel);
35029 if(this.config.initialSize && this.panels.getCount()==1){
35030 this.resizeTo(this.config.initialSize);
35033 this.fireEvent("paneladded", this, panel);
35038 * Returns true if the panel is in this region.
35039 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35040 * @return {Boolean}
35042 hasPanel : function(panel){
35043 if(typeof panel == "object"){ // must be panel obj
35044 panel = panel.getId();
35046 return this.getPanel(panel) ? true : false;
35050 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35051 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35052 * @param {Boolean} preservePanel Overrides the config preservePanel option
35053 * @return {Roo.ContentPanel} The panel that was removed
35055 remove : function(panel, preservePanel){
35056 panel = this.getPanel(panel);
35061 this.fireEvent("beforeremove", this, panel, e);
35062 if(e.cancel === true){
35065 var panelId = panel.getId();
35066 this.panels.removeKey(panelId);
35071 * Returns the panel specified or null if it's not in this region.
35072 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35073 * @return {Roo.ContentPanel}
35075 getPanel : function(id){
35076 if(typeof id == "object"){ // must be panel obj
35079 return this.panels.get(id);
35083 * Returns this regions position (north/south/east/west/center).
35086 getPosition: function(){
35087 return this.position;
35091 * Ext JS Library 1.1.1
35092 * Copyright(c) 2006-2007, Ext JS, LLC.
35094 * Originally Released Under LGPL - original licence link has changed is not relivant.
35097 * <script type="text/javascript">
35101 * @class Roo.bootstrap.layout.Region
35102 * @extends Roo.bootstrap.layout.Basic
35103 * This class represents a region in a layout manager.
35105 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35106 * @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})
35107 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
35108 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
35109 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
35110 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
35111 * @cfg {String} title The title for the region (overrides panel titles)
35112 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
35113 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35114 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
35115 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35116 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
35117 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35118 * the space available, similar to FireFox 1.5 tabs (defaults to false)
35119 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
35120 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
35121 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
35123 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
35124 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
35125 * @cfg {Boolean} disableTabTips True to disable tab tooltips
35126 * @cfg {Number} width For East/West panels
35127 * @cfg {Number} height For North/South panels
35128 * @cfg {Boolean} split To show the splitter
35129 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
35131 * @cfg {string} cls Extra CSS classes to add to region
35133 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35134 * @cfg {string} region the region that it inhabits..
35137 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
35138 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
35140 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
35141 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
35142 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
35144 Roo.bootstrap.layout.Region = function(config)
35146 this.applyConfig(config);
35148 var mgr = config.mgr;
35149 var pos = config.region;
35150 config.skipConfig = true;
35151 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35154 this.onRender(mgr.el);
35157 this.visible = true;
35158 this.collapsed = false;
35159 this.unrendered_panels = [];
35162 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35164 position: '', // set by wrapper (eg. north/south etc..)
35165 unrendered_panels : null, // unrendered panels.
35166 createBody : function(){
35167 /** This region's body element
35168 * @type Roo.Element */
35169 this.bodyEl = this.el.createChild({
35171 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35175 onRender: function(ctr, pos)
35177 var dh = Roo.DomHelper;
35178 /** This region's container element
35179 * @type Roo.Element */
35180 this.el = dh.append(ctr.dom, {
35182 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35184 /** This region's title element
35185 * @type Roo.Element */
35187 this.titleEl = dh.append(this.el.dom,
35190 unselectable: "on",
35191 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35193 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
35194 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35197 this.titleEl.enableDisplayMode();
35198 /** This region's title text element
35199 * @type HTMLElement */
35200 this.titleTextEl = this.titleEl.dom.firstChild;
35201 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35203 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35204 this.closeBtn.enableDisplayMode();
35205 this.closeBtn.on("click", this.closeClicked, this);
35206 this.closeBtn.hide();
35208 this.createBody(this.config);
35209 if(this.config.hideWhenEmpty){
35211 this.on("paneladded", this.validateVisibility, this);
35212 this.on("panelremoved", this.validateVisibility, this);
35214 if(this.autoScroll){
35215 this.bodyEl.setStyle("overflow", "auto");
35217 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35219 //if(c.titlebar !== false){
35220 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35221 this.titleEl.hide();
35223 this.titleEl.show();
35224 if(this.config.title){
35225 this.titleTextEl.innerHTML = this.config.title;
35229 if(this.config.collapsed){
35230 this.collapse(true);
35232 if(this.config.hidden){
35236 if (this.unrendered_panels && this.unrendered_panels.length) {
35237 for (var i =0;i< this.unrendered_panels.length; i++) {
35238 this.add(this.unrendered_panels[i]);
35240 this.unrendered_panels = null;
35246 applyConfig : function(c)
35249 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35250 var dh = Roo.DomHelper;
35251 if(c.titlebar !== false){
35252 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35253 this.collapseBtn.on("click", this.collapse, this);
35254 this.collapseBtn.enableDisplayMode();
35256 if(c.showPin === true || this.showPin){
35257 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35258 this.stickBtn.enableDisplayMode();
35259 this.stickBtn.on("click", this.expand, this);
35260 this.stickBtn.hide();
35265 /** This region's collapsed element
35266 * @type Roo.Element */
35269 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35270 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35273 if(c.floatable !== false){
35274 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35275 this.collapsedEl.on("click", this.collapseClick, this);
35278 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35279 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35280 id: "message", unselectable: "on", style:{"float":"left"}});
35281 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35283 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35284 this.expandBtn.on("click", this.expand, this);
35288 if(this.collapseBtn){
35289 this.collapseBtn.setVisible(c.collapsible == true);
35292 this.cmargins = c.cmargins || this.cmargins ||
35293 (this.position == "west" || this.position == "east" ?
35294 {top: 0, left: 2, right:2, bottom: 0} :
35295 {top: 2, left: 0, right:0, bottom: 2});
35297 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35300 this.bottomTabs = c.tabPosition != "top";
35302 this.autoScroll = c.autoScroll || false;
35307 this.duration = c.duration || .30;
35308 this.slideDuration = c.slideDuration || .45;
35313 * Returns true if this region is currently visible.
35314 * @return {Boolean}
35316 isVisible : function(){
35317 return this.visible;
35321 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35322 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
35324 //setCollapsedTitle : function(title){
35325 // title = title || " ";
35326 // if(this.collapsedTitleTextEl){
35327 // this.collapsedTitleTextEl.innerHTML = title;
35331 getBox : function(){
35333 // if(!this.collapsed){
35334 b = this.el.getBox(false, true);
35336 // b = this.collapsedEl.getBox(false, true);
35341 getMargins : function(){
35342 return this.margins;
35343 //return this.collapsed ? this.cmargins : this.margins;
35346 highlight : function(){
35347 this.el.addClass("x-layout-panel-dragover");
35350 unhighlight : function(){
35351 this.el.removeClass("x-layout-panel-dragover");
35354 updateBox : function(box)
35356 if (!this.bodyEl) {
35357 return; // not rendered yet..
35361 if(!this.collapsed){
35362 this.el.dom.style.left = box.x + "px";
35363 this.el.dom.style.top = box.y + "px";
35364 this.updateBody(box.width, box.height);
35366 this.collapsedEl.dom.style.left = box.x + "px";
35367 this.collapsedEl.dom.style.top = box.y + "px";
35368 this.collapsedEl.setSize(box.width, box.height);
35371 this.tabs.autoSizeTabs();
35375 updateBody : function(w, h)
35378 this.el.setWidth(w);
35379 w -= this.el.getBorderWidth("rl");
35380 if(this.config.adjustments){
35381 w += this.config.adjustments[0];
35384 if(h !== null && h > 0){
35385 this.el.setHeight(h);
35386 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35387 h -= this.el.getBorderWidth("tb");
35388 if(this.config.adjustments){
35389 h += this.config.adjustments[1];
35391 this.bodyEl.setHeight(h);
35393 h = this.tabs.syncHeight(h);
35396 if(this.panelSize){
35397 w = w !== null ? w : this.panelSize.width;
35398 h = h !== null ? h : this.panelSize.height;
35400 if(this.activePanel){
35401 var el = this.activePanel.getEl();
35402 w = w !== null ? w : el.getWidth();
35403 h = h !== null ? h : el.getHeight();
35404 this.panelSize = {width: w, height: h};
35405 this.activePanel.setSize(w, h);
35407 if(Roo.isIE && this.tabs){
35408 this.tabs.el.repaint();
35413 * Returns the container element for this region.
35414 * @return {Roo.Element}
35416 getEl : function(){
35421 * Hides this region.
35424 //if(!this.collapsed){
35425 this.el.dom.style.left = "-2000px";
35428 // this.collapsedEl.dom.style.left = "-2000px";
35429 // this.collapsedEl.hide();
35431 this.visible = false;
35432 this.fireEvent("visibilitychange", this, false);
35436 * Shows this region if it was previously hidden.
35439 //if(!this.collapsed){
35442 // this.collapsedEl.show();
35444 this.visible = true;
35445 this.fireEvent("visibilitychange", this, true);
35448 closeClicked : function(){
35449 if(this.activePanel){
35450 this.remove(this.activePanel);
35454 collapseClick : function(e){
35456 e.stopPropagation();
35459 e.stopPropagation();
35465 * Collapses this region.
35466 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35469 collapse : function(skipAnim, skipCheck = false){
35470 if(this.collapsed) {
35474 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35476 this.collapsed = true;
35478 this.split.el.hide();
35480 if(this.config.animate && skipAnim !== true){
35481 this.fireEvent("invalidated", this);
35482 this.animateCollapse();
35484 this.el.setLocation(-20000,-20000);
35486 this.collapsedEl.show();
35487 this.fireEvent("collapsed", this);
35488 this.fireEvent("invalidated", this);
35494 animateCollapse : function(){
35499 * Expands this region if it was previously collapsed.
35500 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35501 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35504 expand : function(e, skipAnim){
35506 e.stopPropagation();
35508 if(!this.collapsed || this.el.hasActiveFx()) {
35512 this.afterSlideIn();
35515 this.collapsed = false;
35516 if(this.config.animate && skipAnim !== true){
35517 this.animateExpand();
35521 this.split.el.show();
35523 this.collapsedEl.setLocation(-2000,-2000);
35524 this.collapsedEl.hide();
35525 this.fireEvent("invalidated", this);
35526 this.fireEvent("expanded", this);
35530 animateExpand : function(){
35534 initTabs : function()
35536 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35538 var ts = new Roo.bootstrap.panel.Tabs({
35539 el: this.bodyEl.dom,
35540 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35541 disableTooltips: this.config.disableTabTips,
35542 toolbar : this.config.toolbar
35545 if(this.config.hideTabs){
35546 ts.stripWrap.setDisplayed(false);
35549 ts.resizeTabs = this.config.resizeTabs === true;
35550 ts.minTabWidth = this.config.minTabWidth || 40;
35551 ts.maxTabWidth = this.config.maxTabWidth || 250;
35552 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35553 ts.monitorResize = false;
35554 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35555 ts.bodyEl.addClass('roo-layout-tabs-body');
35556 this.panels.each(this.initPanelAsTab, this);
35559 initPanelAsTab : function(panel){
35560 var ti = this.tabs.addTab(
35564 this.config.closeOnTab && panel.isClosable(),
35567 if(panel.tabTip !== undefined){
35568 ti.setTooltip(panel.tabTip);
35570 ti.on("activate", function(){
35571 this.setActivePanel(panel);
35574 if(this.config.closeOnTab){
35575 ti.on("beforeclose", function(t, e){
35577 this.remove(panel);
35581 panel.tabItem = ti;
35586 updatePanelTitle : function(panel, title)
35588 if(this.activePanel == panel){
35589 this.updateTitle(title);
35592 var ti = this.tabs.getTab(panel.getEl().id);
35594 if(panel.tabTip !== undefined){
35595 ti.setTooltip(panel.tabTip);
35600 updateTitle : function(title){
35601 if(this.titleTextEl && !this.config.title){
35602 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
35606 setActivePanel : function(panel)
35608 panel = this.getPanel(panel);
35609 if(this.activePanel && this.activePanel != panel){
35610 if(this.activePanel.setActiveState(false) === false){
35614 this.activePanel = panel;
35615 panel.setActiveState(true);
35616 if(this.panelSize){
35617 panel.setSize(this.panelSize.width, this.panelSize.height);
35620 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35622 this.updateTitle(panel.getTitle());
35624 this.fireEvent("invalidated", this);
35626 this.fireEvent("panelactivated", this, panel);
35630 * Shows the specified panel.
35631 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35632 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35634 showPanel : function(panel)
35636 panel = this.getPanel(panel);
35639 var tab = this.tabs.getTab(panel.getEl().id);
35640 if(tab.isHidden()){
35641 this.tabs.unhideTab(tab.id);
35645 this.setActivePanel(panel);
35652 * Get the active panel for this region.
35653 * @return {Roo.ContentPanel} The active panel or null
35655 getActivePanel : function(){
35656 return this.activePanel;
35659 validateVisibility : function(){
35660 if(this.panels.getCount() < 1){
35661 this.updateTitle(" ");
35662 this.closeBtn.hide();
35665 if(!this.isVisible()){
35672 * Adds the passed ContentPanel(s) to this region.
35673 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35674 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
35676 add : function(panel)
35678 if(arguments.length > 1){
35679 for(var i = 0, len = arguments.length; i < len; i++) {
35680 this.add(arguments[i]);
35685 // if we have not been rendered yet, then we can not really do much of this..
35686 if (!this.bodyEl) {
35687 this.unrendered_panels.push(panel);
35694 if(this.hasPanel(panel)){
35695 this.showPanel(panel);
35698 panel.setRegion(this);
35699 this.panels.add(panel);
35700 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
35701 // sinle panel - no tab...?? would it not be better to render it with the tabs,
35702 // and hide them... ???
35703 this.bodyEl.dom.appendChild(panel.getEl().dom);
35704 if(panel.background !== true){
35705 this.setActivePanel(panel);
35707 this.fireEvent("paneladded", this, panel);
35714 this.initPanelAsTab(panel);
35718 if(panel.background !== true){
35719 this.tabs.activate(panel.getEl().id);
35721 this.fireEvent("paneladded", this, panel);
35726 * Hides the tab for the specified panel.
35727 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35729 hidePanel : function(panel){
35730 if(this.tabs && (panel = this.getPanel(panel))){
35731 this.tabs.hideTab(panel.getEl().id);
35736 * Unhides the tab for a previously hidden panel.
35737 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35739 unhidePanel : function(panel){
35740 if(this.tabs && (panel = this.getPanel(panel))){
35741 this.tabs.unhideTab(panel.getEl().id);
35745 clearPanels : function(){
35746 while(this.panels.getCount() > 0){
35747 this.remove(this.panels.first());
35752 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35753 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35754 * @param {Boolean} preservePanel Overrides the config preservePanel option
35755 * @return {Roo.ContentPanel} The panel that was removed
35757 remove : function(panel, preservePanel)
35759 panel = this.getPanel(panel);
35764 this.fireEvent("beforeremove", this, panel, e);
35765 if(e.cancel === true){
35768 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
35769 var panelId = panel.getId();
35770 this.panels.removeKey(panelId);
35772 document.body.appendChild(panel.getEl().dom);
35775 this.tabs.removeTab(panel.getEl().id);
35776 }else if (!preservePanel){
35777 this.bodyEl.dom.removeChild(panel.getEl().dom);
35779 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
35780 var p = this.panels.first();
35781 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
35782 tempEl.appendChild(p.getEl().dom);
35783 this.bodyEl.update("");
35784 this.bodyEl.dom.appendChild(p.getEl().dom);
35786 this.updateTitle(p.getTitle());
35788 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
35789 this.setActivePanel(p);
35791 panel.setRegion(null);
35792 if(this.activePanel == panel){
35793 this.activePanel = null;
35795 if(this.config.autoDestroy !== false && preservePanel !== true){
35796 try{panel.destroy();}catch(e){}
35798 this.fireEvent("panelremoved", this, panel);
35803 * Returns the TabPanel component used by this region
35804 * @return {Roo.TabPanel}
35806 getTabs : function(){
35810 createTool : function(parentEl, className){
35811 var btn = Roo.DomHelper.append(parentEl, {
35813 cls: "x-layout-tools-button",
35816 cls: "roo-layout-tools-button-inner " + className,
35820 btn.addClassOnOver("roo-layout-tools-button-over");
35825 * Ext JS Library 1.1.1
35826 * Copyright(c) 2006-2007, Ext JS, LLC.
35828 * Originally Released Under LGPL - original licence link has changed is not relivant.
35831 * <script type="text/javascript">
35837 * @class Roo.SplitLayoutRegion
35838 * @extends Roo.LayoutRegion
35839 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
35841 Roo.bootstrap.layout.Split = function(config){
35842 this.cursor = config.cursor;
35843 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
35846 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
35848 splitTip : "Drag to resize.",
35849 collapsibleSplitTip : "Drag to resize. Double click to hide.",
35850 useSplitTips : false,
35852 applyConfig : function(config){
35853 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
35856 onRender : function(ctr,pos) {
35858 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
35859 if(!this.config.split){
35864 var splitEl = Roo.DomHelper.append(ctr.dom, {
35866 id: this.el.id + "-split",
35867 cls: "roo-layout-split roo-layout-split-"+this.position,
35870 /** The SplitBar for this region
35871 * @type Roo.SplitBar */
35872 // does not exist yet...
35873 Roo.log([this.position, this.orientation]);
35875 this.split = new Roo.bootstrap.SplitBar({
35876 dragElement : splitEl,
35877 resizingElement: this.el,
35878 orientation : this.orientation
35881 this.split.on("moved", this.onSplitMove, this);
35882 this.split.useShim = this.config.useShim === true;
35883 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
35884 if(this.useSplitTips){
35885 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
35887 //if(config.collapsible){
35888 // this.split.el.on("dblclick", this.collapse, this);
35891 if(typeof this.config.minSize != "undefined"){
35892 this.split.minSize = this.config.minSize;
35894 if(typeof this.config.maxSize != "undefined"){
35895 this.split.maxSize = this.config.maxSize;
35897 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
35898 this.hideSplitter();
35903 getHMaxSize : function(){
35904 var cmax = this.config.maxSize || 10000;
35905 var center = this.mgr.getRegion("center");
35906 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
35909 getVMaxSize : function(){
35910 var cmax = this.config.maxSize || 10000;
35911 var center = this.mgr.getRegion("center");
35912 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
35915 onSplitMove : function(split, newSize){
35916 this.fireEvent("resized", this, newSize);
35920 * Returns the {@link Roo.SplitBar} for this region.
35921 * @return {Roo.SplitBar}
35923 getSplitBar : function(){
35928 this.hideSplitter();
35929 Roo.bootstrap.layout.Split.superclass.hide.call(this);
35932 hideSplitter : function(){
35934 this.split.el.setLocation(-2000,-2000);
35935 this.split.el.hide();
35941 this.split.el.show();
35943 Roo.bootstrap.layout.Split.superclass.show.call(this);
35946 beforeSlide: function(){
35947 if(Roo.isGecko){// firefox overflow auto bug workaround
35948 this.bodyEl.clip();
35950 this.tabs.bodyEl.clip();
35952 if(this.activePanel){
35953 this.activePanel.getEl().clip();
35955 if(this.activePanel.beforeSlide){
35956 this.activePanel.beforeSlide();
35962 afterSlide : function(){
35963 if(Roo.isGecko){// firefox overflow auto bug workaround
35964 this.bodyEl.unclip();
35966 this.tabs.bodyEl.unclip();
35968 if(this.activePanel){
35969 this.activePanel.getEl().unclip();
35970 if(this.activePanel.afterSlide){
35971 this.activePanel.afterSlide();
35977 initAutoHide : function(){
35978 if(this.autoHide !== false){
35979 if(!this.autoHideHd){
35980 var st = new Roo.util.DelayedTask(this.slideIn, this);
35981 this.autoHideHd = {
35982 "mouseout": function(e){
35983 if(!e.within(this.el, true)){
35987 "mouseover" : function(e){
35993 this.el.on(this.autoHideHd);
35997 clearAutoHide : function(){
35998 if(this.autoHide !== false){
35999 this.el.un("mouseout", this.autoHideHd.mouseout);
36000 this.el.un("mouseover", this.autoHideHd.mouseover);
36004 clearMonitor : function(){
36005 Roo.get(document).un("click", this.slideInIf, this);
36008 // these names are backwards but not changed for compat
36009 slideOut : function(){
36010 if(this.isSlid || this.el.hasActiveFx()){
36013 this.isSlid = true;
36014 if(this.collapseBtn){
36015 this.collapseBtn.hide();
36017 this.closeBtnState = this.closeBtn.getStyle('display');
36018 this.closeBtn.hide();
36020 this.stickBtn.show();
36023 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36024 this.beforeSlide();
36025 this.el.setStyle("z-index", 10001);
36026 this.el.slideIn(this.getSlideAnchor(), {
36027 callback: function(){
36029 this.initAutoHide();
36030 Roo.get(document).on("click", this.slideInIf, this);
36031 this.fireEvent("slideshow", this);
36038 afterSlideIn : function(){
36039 this.clearAutoHide();
36040 this.isSlid = false;
36041 this.clearMonitor();
36042 this.el.setStyle("z-index", "");
36043 if(this.collapseBtn){
36044 this.collapseBtn.show();
36046 this.closeBtn.setStyle('display', this.closeBtnState);
36048 this.stickBtn.hide();
36050 this.fireEvent("slidehide", this);
36053 slideIn : function(cb){
36054 if(!this.isSlid || this.el.hasActiveFx()){
36058 this.isSlid = false;
36059 this.beforeSlide();
36060 this.el.slideOut(this.getSlideAnchor(), {
36061 callback: function(){
36062 this.el.setLeftTop(-10000, -10000);
36064 this.afterSlideIn();
36072 slideInIf : function(e){
36073 if(!e.within(this.el)){
36078 animateCollapse : function(){
36079 this.beforeSlide();
36080 this.el.setStyle("z-index", 20000);
36081 var anchor = this.getSlideAnchor();
36082 this.el.slideOut(anchor, {
36083 callback : function(){
36084 this.el.setStyle("z-index", "");
36085 this.collapsedEl.slideIn(anchor, {duration:.3});
36087 this.el.setLocation(-10000,-10000);
36089 this.fireEvent("collapsed", this);
36096 animateExpand : function(){
36097 this.beforeSlide();
36098 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36099 this.el.setStyle("z-index", 20000);
36100 this.collapsedEl.hide({
36103 this.el.slideIn(this.getSlideAnchor(), {
36104 callback : function(){
36105 this.el.setStyle("z-index", "");
36108 this.split.el.show();
36110 this.fireEvent("invalidated", this);
36111 this.fireEvent("expanded", this);
36139 getAnchor : function(){
36140 return this.anchors[this.position];
36143 getCollapseAnchor : function(){
36144 return this.canchors[this.position];
36147 getSlideAnchor : function(){
36148 return this.sanchors[this.position];
36151 getAlignAdj : function(){
36152 var cm = this.cmargins;
36153 switch(this.position){
36169 getExpandAdj : function(){
36170 var c = this.collapsedEl, cm = this.cmargins;
36171 switch(this.position){
36173 return [-(cm.right+c.getWidth()+cm.left), 0];
36176 return [cm.right+c.getWidth()+cm.left, 0];
36179 return [0, -(cm.top+cm.bottom+c.getHeight())];
36182 return [0, cm.top+cm.bottom+c.getHeight()];
36188 * Ext JS Library 1.1.1
36189 * Copyright(c) 2006-2007, Ext JS, LLC.
36191 * Originally Released Under LGPL - original licence link has changed is not relivant.
36194 * <script type="text/javascript">
36197 * These classes are private internal classes
36199 Roo.bootstrap.layout.Center = function(config){
36200 config.region = "center";
36201 Roo.bootstrap.layout.Region.call(this, config);
36202 this.visible = true;
36203 this.minWidth = config.minWidth || 20;
36204 this.minHeight = config.minHeight || 20;
36207 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36209 // center panel can't be hidden
36213 // center panel can't be hidden
36216 getMinWidth: function(){
36217 return this.minWidth;
36220 getMinHeight: function(){
36221 return this.minHeight;
36234 Roo.bootstrap.layout.North = function(config)
36236 config.region = 'north';
36237 config.cursor = 'n-resize';
36239 Roo.bootstrap.layout.Split.call(this, config);
36243 this.split.placement = Roo.bootstrap.SplitBar.TOP;
36244 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36245 this.split.el.addClass("roo-layout-split-v");
36247 var size = config.initialSize || config.height;
36248 if(typeof size != "undefined"){
36249 this.el.setHeight(size);
36252 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36254 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36258 getBox : function(){
36259 if(this.collapsed){
36260 return this.collapsedEl.getBox();
36262 var box = this.el.getBox();
36264 box.height += this.split.el.getHeight();
36269 updateBox : function(box){
36270 if(this.split && !this.collapsed){
36271 box.height -= this.split.el.getHeight();
36272 this.split.el.setLeft(box.x);
36273 this.split.el.setTop(box.y+box.height);
36274 this.split.el.setWidth(box.width);
36276 if(this.collapsed){
36277 this.updateBody(box.width, null);
36279 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36287 Roo.bootstrap.layout.South = function(config){
36288 config.region = 'south';
36289 config.cursor = 's-resize';
36290 Roo.bootstrap.layout.Split.call(this, config);
36292 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36293 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36294 this.split.el.addClass("roo-layout-split-v");
36296 var size = config.initialSize || config.height;
36297 if(typeof size != "undefined"){
36298 this.el.setHeight(size);
36302 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36303 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36304 getBox : function(){
36305 if(this.collapsed){
36306 return this.collapsedEl.getBox();
36308 var box = this.el.getBox();
36310 var sh = this.split.el.getHeight();
36317 updateBox : function(box){
36318 if(this.split && !this.collapsed){
36319 var sh = this.split.el.getHeight();
36322 this.split.el.setLeft(box.x);
36323 this.split.el.setTop(box.y-sh);
36324 this.split.el.setWidth(box.width);
36326 if(this.collapsed){
36327 this.updateBody(box.width, null);
36329 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36333 Roo.bootstrap.layout.East = function(config){
36334 config.region = "east";
36335 config.cursor = "e-resize";
36336 Roo.bootstrap.layout.Split.call(this, config);
36338 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36339 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36340 this.split.el.addClass("roo-layout-split-h");
36342 var size = config.initialSize || config.width;
36343 if(typeof size != "undefined"){
36344 this.el.setWidth(size);
36347 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36348 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36349 getBox : function(){
36350 if(this.collapsed){
36351 return this.collapsedEl.getBox();
36353 var box = this.el.getBox();
36355 var sw = this.split.el.getWidth();
36362 updateBox : function(box){
36363 if(this.split && !this.collapsed){
36364 var sw = this.split.el.getWidth();
36366 this.split.el.setLeft(box.x);
36367 this.split.el.setTop(box.y);
36368 this.split.el.setHeight(box.height);
36371 if(this.collapsed){
36372 this.updateBody(null, box.height);
36374 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36378 Roo.bootstrap.layout.West = function(config){
36379 config.region = "west";
36380 config.cursor = "w-resize";
36382 Roo.bootstrap.layout.Split.call(this, config);
36384 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36385 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36386 this.split.el.addClass("roo-layout-split-h");
36390 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36391 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36393 onRender: function(ctr, pos)
36395 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36396 var size = this.config.initialSize || this.config.width;
36397 if(typeof size != "undefined"){
36398 this.el.setWidth(size);
36402 getBox : function(){
36403 if(this.collapsed){
36404 return this.collapsedEl.getBox();
36406 var box = this.el.getBox();
36408 box.width += this.split.el.getWidth();
36413 updateBox : function(box){
36414 if(this.split && !this.collapsed){
36415 var sw = this.split.el.getWidth();
36417 this.split.el.setLeft(box.x+box.width);
36418 this.split.el.setTop(box.y);
36419 this.split.el.setHeight(box.height);
36421 if(this.collapsed){
36422 this.updateBody(null, box.height);
36424 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36427 Roo.namespace("Roo.bootstrap.panel");/*
36429 * Ext JS Library 1.1.1
36430 * Copyright(c) 2006-2007, Ext JS, LLC.
36432 * Originally Released Under LGPL - original licence link has changed is not relivant.
36435 * <script type="text/javascript">
36438 * @class Roo.ContentPanel
36439 * @extends Roo.util.Observable
36440 * A basic ContentPanel element.
36441 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
36442 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
36443 * @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
36444 * @cfg {Boolean} closable True if the panel can be closed/removed
36445 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
36446 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36447 * @cfg {Toolbar} toolbar A toolbar for this panel
36448 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
36449 * @cfg {String} title The title for this panel
36450 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36451 * @cfg {String} url Calls {@link #setUrl} with this value
36452 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36453 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
36454 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
36455 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
36456 * @cfg {Boolean} badges render the badges
36459 * Create a new ContentPanel.
36460 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36461 * @param {String/Object} config A string to set only the title or a config object
36462 * @param {String} content (optional) Set the HTML content for this panel
36463 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36465 Roo.bootstrap.panel.Content = function( config){
36467 this.tpl = config.tpl || false;
36469 var el = config.el;
36470 var content = config.content;
36472 if(config.autoCreate){ // xtype is available if this is called from factory
36475 this.el = Roo.get(el);
36476 if(!this.el && config && config.autoCreate){
36477 if(typeof config.autoCreate == "object"){
36478 if(!config.autoCreate.id){
36479 config.autoCreate.id = config.id||el;
36481 this.el = Roo.DomHelper.append(document.body,
36482 config.autoCreate, true);
36484 var elcfg = { tag: "div",
36485 cls: "roo-layout-inactive-content",
36489 elcfg.html = config.html;
36493 this.el = Roo.DomHelper.append(document.body, elcfg , true);
36496 this.closable = false;
36497 this.loaded = false;
36498 this.active = false;
36501 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36503 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36505 this.wrapEl = this.el; //this.el.wrap();
36507 if (config.toolbar.items) {
36508 ti = config.toolbar.items ;
36509 delete config.toolbar.items ;
36513 this.toolbar.render(this.wrapEl, 'before');
36514 for(var i =0;i < ti.length;i++) {
36515 // Roo.log(['add child', items[i]]);
36516 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36518 this.toolbar.items = nitems;
36519 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36520 delete config.toolbar;
36524 // xtype created footer. - not sure if will work as we normally have to render first..
36525 if (this.footer && !this.footer.el && this.footer.xtype) {
36526 if (!this.wrapEl) {
36527 this.wrapEl = this.el.wrap();
36530 this.footer.container = this.wrapEl.createChild();
36532 this.footer = Roo.factory(this.footer, Roo);
36537 if(typeof config == "string"){
36538 this.title = config;
36540 Roo.apply(this, config);
36544 this.resizeEl = Roo.get(this.resizeEl, true);
36546 this.resizeEl = this.el;
36548 // handle view.xtype
36556 * Fires when this panel is activated.
36557 * @param {Roo.ContentPanel} this
36561 * @event deactivate
36562 * Fires when this panel is activated.
36563 * @param {Roo.ContentPanel} this
36565 "deactivate" : true,
36569 * Fires when this panel is resized if fitToFrame is true.
36570 * @param {Roo.ContentPanel} this
36571 * @param {Number} width The width after any component adjustments
36572 * @param {Number} height The height after any component adjustments
36578 * Fires when this tab is created
36579 * @param {Roo.ContentPanel} this
36590 if(this.autoScroll){
36591 this.resizeEl.setStyle("overflow", "auto");
36593 // fix randome scrolling
36594 //this.el.on('scroll', function() {
36595 // Roo.log('fix random scolling');
36596 // this.scrollTo('top',0);
36599 content = content || this.content;
36601 this.setContent(content);
36603 if(config && config.url){
36604 this.setUrl(this.url, this.params, this.loadOnce);
36609 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36611 if (this.view && typeof(this.view.xtype) != 'undefined') {
36612 this.view.el = this.el.appendChild(document.createElement("div"));
36613 this.view = Roo.factory(this.view);
36614 this.view.render && this.view.render(false, '');
36618 this.fireEvent('render', this);
36621 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36625 setRegion : function(region){
36626 this.region = region;
36627 this.setActiveClass(region && !this.background);
36631 setActiveClass: function(state)
36634 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36635 this.el.setStyle('position','relative');
36637 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36638 this.el.setStyle('position', 'absolute');
36643 * Returns the toolbar for this Panel if one was configured.
36644 * @return {Roo.Toolbar}
36646 getToolbar : function(){
36647 return this.toolbar;
36650 setActiveState : function(active)
36652 this.active = active;
36653 this.setActiveClass(active);
36655 if(this.fireEvent("deactivate", this) === false){
36660 this.fireEvent("activate", this);
36664 * Updates this panel's element
36665 * @param {String} content The new content
36666 * @param {Boolean} loadScripts (optional) true to look for and process scripts
36668 setContent : function(content, loadScripts){
36669 this.el.update(content, loadScripts);
36672 ignoreResize : function(w, h){
36673 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
36676 this.lastSize = {width: w, height: h};
36681 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
36682 * @return {Roo.UpdateManager} The UpdateManager
36684 getUpdateManager : function(){
36685 return this.el.getUpdateManager();
36688 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
36689 * @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:
36692 url: "your-url.php",
36693 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
36694 callback: yourFunction,
36695 scope: yourObject, //(optional scope)
36698 text: "Loading...",
36703 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
36704 * 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.
36705 * @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}
36706 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
36707 * @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.
36708 * @return {Roo.ContentPanel} this
36711 var um = this.el.getUpdateManager();
36712 um.update.apply(um, arguments);
36718 * 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.
36719 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
36720 * @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)
36721 * @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)
36722 * @return {Roo.UpdateManager} The UpdateManager
36724 setUrl : function(url, params, loadOnce){
36725 if(this.refreshDelegate){
36726 this.removeListener("activate", this.refreshDelegate);
36728 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
36729 this.on("activate", this.refreshDelegate);
36730 return this.el.getUpdateManager();
36733 _handleRefresh : function(url, params, loadOnce){
36734 if(!loadOnce || !this.loaded){
36735 var updater = this.el.getUpdateManager();
36736 updater.update(url, params, this._setLoaded.createDelegate(this));
36740 _setLoaded : function(){
36741 this.loaded = true;
36745 * Returns this panel's id
36748 getId : function(){
36753 * Returns this panel's element - used by regiosn to add.
36754 * @return {Roo.Element}
36756 getEl : function(){
36757 return this.wrapEl || this.el;
36762 adjustForComponents : function(width, height)
36764 //Roo.log('adjustForComponents ');
36765 if(this.resizeEl != this.el){
36766 width -= this.el.getFrameWidth('lr');
36767 height -= this.el.getFrameWidth('tb');
36770 var te = this.toolbar.getEl();
36771 te.setWidth(width);
36772 height -= te.getHeight();
36775 var te = this.footer.getEl();
36776 te.setWidth(width);
36777 height -= te.getHeight();
36781 if(this.adjustments){
36782 width += this.adjustments[0];
36783 height += this.adjustments[1];
36785 return {"width": width, "height": height};
36788 setSize : function(width, height){
36789 if(this.fitToFrame && !this.ignoreResize(width, height)){
36790 if(this.fitContainer && this.resizeEl != this.el){
36791 this.el.setSize(width, height);
36793 var size = this.adjustForComponents(width, height);
36794 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
36795 this.fireEvent('resize', this, size.width, size.height);
36800 * Returns this panel's title
36803 getTitle : function(){
36805 if (typeof(this.title) != 'object') {
36810 for (var k in this.title) {
36811 if (!this.title.hasOwnProperty(k)) {
36815 if (k.indexOf('-') >= 0) {
36816 var s = k.split('-');
36817 for (var i = 0; i<s.length; i++) {
36818 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
36821 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
36828 * Set this panel's title
36829 * @param {String} title
36831 setTitle : function(title){
36832 this.title = title;
36834 this.region.updatePanelTitle(this, title);
36839 * Returns true is this panel was configured to be closable
36840 * @return {Boolean}
36842 isClosable : function(){
36843 return this.closable;
36846 beforeSlide : function(){
36848 this.resizeEl.clip();
36851 afterSlide : function(){
36853 this.resizeEl.unclip();
36857 * Force a content refresh from the URL specified in the {@link #setUrl} method.
36858 * Will fail silently if the {@link #setUrl} method has not been called.
36859 * This does not activate the panel, just updates its content.
36861 refresh : function(){
36862 if(this.refreshDelegate){
36863 this.loaded = false;
36864 this.refreshDelegate();
36869 * Destroys this panel
36871 destroy : function(){
36872 this.el.removeAllListeners();
36873 var tempEl = document.createElement("span");
36874 tempEl.appendChild(this.el.dom);
36875 tempEl.innerHTML = "";
36881 * form - if the content panel contains a form - this is a reference to it.
36882 * @type {Roo.form.Form}
36886 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
36887 * This contains a reference to it.
36893 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
36903 * @param {Object} cfg Xtype definition of item to add.
36907 getChildContainer: function () {
36908 return this.getEl();
36913 var ret = new Roo.factory(cfg);
36918 if (cfg.xtype.match(/^Form$/)) {
36921 //if (this.footer) {
36922 // el = this.footer.container.insertSibling(false, 'before');
36924 el = this.el.createChild();
36927 this.form = new Roo.form.Form(cfg);
36930 if ( this.form.allItems.length) {
36931 this.form.render(el.dom);
36935 // should only have one of theses..
36936 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
36937 // views.. should not be just added - used named prop 'view''
36939 cfg.el = this.el.appendChild(document.createElement("div"));
36942 var ret = new Roo.factory(cfg);
36944 ret.render && ret.render(false, ''); // render blank..
36954 * @class Roo.bootstrap.panel.Grid
36955 * @extends Roo.bootstrap.panel.Content
36957 * Create a new GridPanel.
36958 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
36959 * @param {Object} config A the config object
36965 Roo.bootstrap.panel.Grid = function(config)
36969 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
36970 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
36972 config.el = this.wrapper;
36973 //this.el = this.wrapper;
36975 if (config.container) {
36976 // ctor'ed from a Border/panel.grid
36979 this.wrapper.setStyle("overflow", "hidden");
36980 this.wrapper.addClass('roo-grid-container');
36985 if(config.toolbar){
36986 var tool_el = this.wrapper.createChild();
36987 this.toolbar = Roo.factory(config.toolbar);
36989 if (config.toolbar.items) {
36990 ti = config.toolbar.items ;
36991 delete config.toolbar.items ;
36995 this.toolbar.render(tool_el);
36996 for(var i =0;i < ti.length;i++) {
36997 // Roo.log(['add child', items[i]]);
36998 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37000 this.toolbar.items = nitems;
37002 delete config.toolbar;
37005 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37006 config.grid.scrollBody = true;;
37007 config.grid.monitorWindowResize = false; // turn off autosizing
37008 config.grid.autoHeight = false;
37009 config.grid.autoWidth = false;
37011 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37013 if (config.background) {
37014 // render grid on panel activation (if panel background)
37015 this.on('activate', function(gp) {
37016 if (!gp.grid.rendered) {
37017 gp.grid.render(this.wrapper);
37018 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37023 this.grid.render(this.wrapper);
37024 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37027 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37028 // ??? needed ??? config.el = this.wrapper;
37033 // xtype created footer. - not sure if will work as we normally have to render first..
37034 if (this.footer && !this.footer.el && this.footer.xtype) {
37036 var ctr = this.grid.getView().getFooterPanel(true);
37037 this.footer.dataSource = this.grid.dataSource;
37038 this.footer = Roo.factory(this.footer, Roo);
37039 this.footer.render(ctr);
37049 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37050 getId : function(){
37051 return this.grid.id;
37055 * Returns the grid for this panel
37056 * @return {Roo.bootstrap.Table}
37058 getGrid : function(){
37062 setSize : function(width, height){
37063 if(!this.ignoreResize(width, height)){
37064 var grid = this.grid;
37065 var size = this.adjustForComponents(width, height);
37066 var gridel = grid.getGridEl();
37067 gridel.setSize(size.width, size.height);
37069 var thd = grid.getGridEl().select('thead',true).first();
37070 var tbd = grid.getGridEl().select('tbody', true).first();
37072 tbd.setSize(width, height - thd.getHeight());
37081 beforeSlide : function(){
37082 this.grid.getView().scroller.clip();
37085 afterSlide : function(){
37086 this.grid.getView().scroller.unclip();
37089 destroy : function(){
37090 this.grid.destroy();
37092 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
37097 * @class Roo.bootstrap.panel.Nest
37098 * @extends Roo.bootstrap.panel.Content
37100 * Create a new Panel, that can contain a layout.Border.
37103 * @param {Roo.BorderLayout} layout The layout for this panel
37104 * @param {String/Object} config A string to set only the title or a config object
37106 Roo.bootstrap.panel.Nest = function(config)
37108 // construct with only one argument..
37109 /* FIXME - implement nicer consturctors
37110 if (layout.layout) {
37112 layout = config.layout;
37113 delete config.layout;
37115 if (layout.xtype && !layout.getEl) {
37116 // then layout needs constructing..
37117 layout = Roo.factory(layout, Roo);
37121 config.el = config.layout.getEl();
37123 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37125 config.layout.monitorWindowResize = false; // turn off autosizing
37126 this.layout = config.layout;
37127 this.layout.getEl().addClass("roo-layout-nested-layout");
37134 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37136 setSize : function(width, height){
37137 if(!this.ignoreResize(width, height)){
37138 var size = this.adjustForComponents(width, height);
37139 var el = this.layout.getEl();
37140 if (size.height < 1) {
37141 el.setWidth(size.width);
37143 el.setSize(size.width, size.height);
37145 var touch = el.dom.offsetWidth;
37146 this.layout.layout();
37147 // ie requires a double layout on the first pass
37148 if(Roo.isIE && !this.initialized){
37149 this.initialized = true;
37150 this.layout.layout();
37155 // activate all subpanels if not currently active..
37157 setActiveState : function(active){
37158 this.active = active;
37159 this.setActiveClass(active);
37162 this.fireEvent("deactivate", this);
37166 this.fireEvent("activate", this);
37167 // not sure if this should happen before or after..
37168 if (!this.layout) {
37169 return; // should not happen..
37172 for (var r in this.layout.regions) {
37173 reg = this.layout.getRegion(r);
37174 if (reg.getActivePanel()) {
37175 //reg.showPanel(reg.getActivePanel()); // force it to activate..
37176 reg.setActivePanel(reg.getActivePanel());
37179 if (!reg.panels.length) {
37182 reg.showPanel(reg.getPanel(0));
37191 * Returns the nested BorderLayout for this panel
37192 * @return {Roo.BorderLayout}
37194 getLayout : function(){
37195 return this.layout;
37199 * Adds a xtype elements to the layout of the nested panel
37203 xtype : 'ContentPanel',
37210 xtype : 'NestedLayoutPanel',
37216 items : [ ... list of content panels or nested layout panels.. ]
37220 * @param {Object} cfg Xtype definition of item to add.
37222 addxtype : function(cfg) {
37223 return this.layout.addxtype(cfg);
37228 * Ext JS Library 1.1.1
37229 * Copyright(c) 2006-2007, Ext JS, LLC.
37231 * Originally Released Under LGPL - original licence link has changed is not relivant.
37234 * <script type="text/javascript">
37237 * @class Roo.TabPanel
37238 * @extends Roo.util.Observable
37239 * A lightweight tab container.
37243 // basic tabs 1, built from existing content
37244 var tabs = new Roo.TabPanel("tabs1");
37245 tabs.addTab("script", "View Script");
37246 tabs.addTab("markup", "View Markup");
37247 tabs.activate("script");
37249 // more advanced tabs, built from javascript
37250 var jtabs = new Roo.TabPanel("jtabs");
37251 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37253 // set up the UpdateManager
37254 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37255 var updater = tab2.getUpdateManager();
37256 updater.setDefaultUrl("ajax1.htm");
37257 tab2.on('activate', updater.refresh, updater, true);
37259 // Use setUrl for Ajax loading
37260 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37261 tab3.setUrl("ajax2.htm", null, true);
37264 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37267 jtabs.activate("jtabs-1");
37270 * Create a new TabPanel.
37271 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37272 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37274 Roo.bootstrap.panel.Tabs = function(config){
37276 * The container element for this TabPanel.
37277 * @type Roo.Element
37279 this.el = Roo.get(config.el);
37282 if(typeof config == "boolean"){
37283 this.tabPosition = config ? "bottom" : "top";
37285 Roo.apply(this, config);
37289 if(this.tabPosition == "bottom"){
37290 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37291 this.el.addClass("roo-tabs-bottom");
37293 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37294 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37295 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37297 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37299 if(this.tabPosition != "bottom"){
37300 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37301 * @type Roo.Element
37303 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37304 this.el.addClass("roo-tabs-top");
37308 this.bodyEl.setStyle("position", "relative");
37310 this.active = null;
37311 this.activateDelegate = this.activate.createDelegate(this);
37316 * Fires when the active tab changes
37317 * @param {Roo.TabPanel} this
37318 * @param {Roo.TabPanelItem} activePanel The new active tab
37322 * @event beforetabchange
37323 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37324 * @param {Roo.TabPanel} this
37325 * @param {Object} e Set cancel to true on this object to cancel the tab change
37326 * @param {Roo.TabPanelItem} tab The tab being changed to
37328 "beforetabchange" : true
37331 Roo.EventManager.onWindowResize(this.onResize, this);
37332 this.cpad = this.el.getPadding("lr");
37333 this.hiddenCount = 0;
37336 // toolbar on the tabbar support...
37337 if (this.toolbar) {
37338 alert("no toolbar support yet");
37339 this.toolbar = false;
37341 var tcfg = this.toolbar;
37342 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
37343 this.toolbar = new Roo.Toolbar(tcfg);
37344 if (Roo.isSafari) {
37345 var tbl = tcfg.container.child('table', true);
37346 tbl.setAttribute('width', '100%');
37354 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37357 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37359 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37361 tabPosition : "top",
37363 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37365 currentTabWidth : 0,
37367 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37371 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37375 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37377 preferredTabWidth : 175,
37379 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37381 resizeTabs : false,
37383 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37385 monitorResize : true,
37387 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
37392 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37393 * @param {String} id The id of the div to use <b>or create</b>
37394 * @param {String} text The text for the tab
37395 * @param {String} content (optional) Content to put in the TabPanelItem body
37396 * @param {Boolean} closable (optional) True to create a close icon on the tab
37397 * @return {Roo.TabPanelItem} The created TabPanelItem
37399 addTab : function(id, text, content, closable, tpl)
37401 var item = new Roo.bootstrap.panel.TabItem({
37405 closable : closable,
37408 this.addTabItem(item);
37410 item.setContent(content);
37416 * Returns the {@link Roo.TabPanelItem} with the specified id/index
37417 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37418 * @return {Roo.TabPanelItem}
37420 getTab : function(id){
37421 return this.items[id];
37425 * Hides the {@link Roo.TabPanelItem} with the specified id/index
37426 * @param {String/Number} id The id or index of the TabPanelItem to hide.
37428 hideTab : function(id){
37429 var t = this.items[id];
37432 this.hiddenCount++;
37433 this.autoSizeTabs();
37438 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37439 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37441 unhideTab : function(id){
37442 var t = this.items[id];
37444 t.setHidden(false);
37445 this.hiddenCount--;
37446 this.autoSizeTabs();
37451 * Adds an existing {@link Roo.TabPanelItem}.
37452 * @param {Roo.TabPanelItem} item The TabPanelItem to add
37454 addTabItem : function(item){
37455 this.items[item.id] = item;
37456 this.items.push(item);
37457 // if(this.resizeTabs){
37458 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37459 // this.autoSizeTabs();
37461 // item.autoSize();
37466 * Removes a {@link Roo.TabPanelItem}.
37467 * @param {String/Number} id The id or index of the TabPanelItem to remove.
37469 removeTab : function(id){
37470 var items = this.items;
37471 var tab = items[id];
37472 if(!tab) { return; }
37473 var index = items.indexOf(tab);
37474 if(this.active == tab && items.length > 1){
37475 var newTab = this.getNextAvailable(index);
37480 this.stripEl.dom.removeChild(tab.pnode.dom);
37481 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37482 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37484 items.splice(index, 1);
37485 delete this.items[tab.id];
37486 tab.fireEvent("close", tab);
37487 tab.purgeListeners();
37488 this.autoSizeTabs();
37491 getNextAvailable : function(start){
37492 var items = this.items;
37494 // look for a next tab that will slide over to
37495 // replace the one being removed
37496 while(index < items.length){
37497 var item = items[++index];
37498 if(item && !item.isHidden()){
37502 // if one isn't found select the previous tab (on the left)
37505 var item = items[--index];
37506 if(item && !item.isHidden()){
37514 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37515 * @param {String/Number} id The id or index of the TabPanelItem to disable.
37517 disableTab : function(id){
37518 var tab = this.items[id];
37519 if(tab && this.active != tab){
37525 * Enables a {@link Roo.TabPanelItem} that is disabled.
37526 * @param {String/Number} id The id or index of the TabPanelItem to enable.
37528 enableTab : function(id){
37529 var tab = this.items[id];
37534 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37535 * @param {String/Number} id The id or index of the TabPanelItem to activate.
37536 * @return {Roo.TabPanelItem} The TabPanelItem.
37538 activate : function(id){
37539 var tab = this.items[id];
37543 if(tab == this.active || tab.disabled){
37547 this.fireEvent("beforetabchange", this, e, tab);
37548 if(e.cancel !== true && !tab.disabled){
37550 this.active.hide();
37552 this.active = this.items[id];
37553 this.active.show();
37554 this.fireEvent("tabchange", this, this.active);
37560 * Gets the active {@link Roo.TabPanelItem}.
37561 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37563 getActiveTab : function(){
37564 return this.active;
37568 * Updates the tab body element to fit the height of the container element
37569 * for overflow scrolling
37570 * @param {Number} targetHeight (optional) Override the starting height from the elements height
37572 syncHeight : function(targetHeight){
37573 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37574 var bm = this.bodyEl.getMargins();
37575 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37576 this.bodyEl.setHeight(newHeight);
37580 onResize : function(){
37581 if(this.monitorResize){
37582 this.autoSizeTabs();
37587 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37589 beginUpdate : function(){
37590 this.updating = true;
37594 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37596 endUpdate : function(){
37597 this.updating = false;
37598 this.autoSizeTabs();
37602 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37604 autoSizeTabs : function(){
37605 var count = this.items.length;
37606 var vcount = count - this.hiddenCount;
37607 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37610 var w = Math.max(this.el.getWidth() - this.cpad, 10);
37611 var availWidth = Math.floor(w / vcount);
37612 var b = this.stripBody;
37613 if(b.getWidth() > w){
37614 var tabs = this.items;
37615 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37616 if(availWidth < this.minTabWidth){
37617 /*if(!this.sleft){ // incomplete scrolling code
37618 this.createScrollButtons();
37621 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37624 if(this.currentTabWidth < this.preferredTabWidth){
37625 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37631 * Returns the number of tabs in this TabPanel.
37634 getCount : function(){
37635 return this.items.length;
37639 * Resizes all the tabs to the passed width
37640 * @param {Number} The new width
37642 setTabWidth : function(width){
37643 this.currentTabWidth = width;
37644 for(var i = 0, len = this.items.length; i < len; i++) {
37645 if(!this.items[i].isHidden()) {
37646 this.items[i].setWidth(width);
37652 * Destroys this TabPanel
37653 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
37655 destroy : function(removeEl){
37656 Roo.EventManager.removeResizeListener(this.onResize, this);
37657 for(var i = 0, len = this.items.length; i < len; i++){
37658 this.items[i].purgeListeners();
37660 if(removeEl === true){
37661 this.el.update("");
37666 createStrip : function(container)
37668 var strip = document.createElement("nav");
37669 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
37670 container.appendChild(strip);
37674 createStripList : function(strip)
37676 // div wrapper for retard IE
37677 // returns the "tr" element.
37678 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
37679 //'<div class="x-tabs-strip-wrap">'+
37680 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
37681 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
37682 return strip.firstChild; //.firstChild.firstChild.firstChild;
37684 createBody : function(container)
37686 var body = document.createElement("div");
37687 Roo.id(body, "tab-body");
37688 //Roo.fly(body).addClass("x-tabs-body");
37689 Roo.fly(body).addClass("tab-content");
37690 container.appendChild(body);
37693 createItemBody :function(bodyEl, id){
37694 var body = Roo.getDom(id);
37696 body = document.createElement("div");
37699 //Roo.fly(body).addClass("x-tabs-item-body");
37700 Roo.fly(body).addClass("tab-pane");
37701 bodyEl.insertBefore(body, bodyEl.firstChild);
37705 createStripElements : function(stripEl, text, closable, tpl)
37707 var td = document.createElement("li"); // was td..
37710 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
37713 stripEl.appendChild(td);
37715 td.className = "x-tabs-closable";
37716 if(!this.closeTpl){
37717 this.closeTpl = new Roo.Template(
37718 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37719 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
37720 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
37723 var el = this.closeTpl.overwrite(td, {"text": text});
37724 var close = el.getElementsByTagName("div")[0];
37725 var inner = el.getElementsByTagName("em")[0];
37726 return {"el": el, "close": close, "inner": inner};
37729 // not sure what this is..
37730 // if(!this.tabTpl){
37731 //this.tabTpl = new Roo.Template(
37732 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37733 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
37735 // this.tabTpl = new Roo.Template(
37736 // '<a href="#">' +
37737 // '<span unselectable="on"' +
37738 // (this.disableTooltips ? '' : ' title="{text}"') +
37739 // ' >{text}</span></a>'
37745 var template = tpl || this.tabTpl || false;
37749 template = new Roo.Template(
37751 '<span unselectable="on"' +
37752 (this.disableTooltips ? '' : ' title="{text}"') +
37753 ' >{text}</span></a>'
37757 switch (typeof(template)) {
37761 template = new Roo.Template(template);
37767 var el = template.overwrite(td, {"text": text});
37769 var inner = el.getElementsByTagName("span")[0];
37771 return {"el": el, "inner": inner};
37779 * @class Roo.TabPanelItem
37780 * @extends Roo.util.Observable
37781 * Represents an individual item (tab plus body) in a TabPanel.
37782 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
37783 * @param {String} id The id of this TabPanelItem
37784 * @param {String} text The text for the tab of this TabPanelItem
37785 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
37787 Roo.bootstrap.panel.TabItem = function(config){
37789 * The {@link Roo.TabPanel} this TabPanelItem belongs to
37790 * @type Roo.TabPanel
37792 this.tabPanel = config.panel;
37794 * The id for this TabPanelItem
37797 this.id = config.id;
37799 this.disabled = false;
37801 this.text = config.text;
37803 this.loaded = false;
37804 this.closable = config.closable;
37807 * The body element for this TabPanelItem.
37808 * @type Roo.Element
37810 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
37811 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
37812 this.bodyEl.setStyle("display", "block");
37813 this.bodyEl.setStyle("zoom", "1");
37814 //this.hideAction();
37816 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
37818 this.el = Roo.get(els.el);
37819 this.inner = Roo.get(els.inner, true);
37820 this.textEl = Roo.get(this.el.dom.firstChild, true);
37821 this.pnode = Roo.get(els.el.parentNode, true);
37822 // this.el.on("mousedown", this.onTabMouseDown, this);
37823 this.el.on("click", this.onTabClick, this);
37825 if(config.closable){
37826 var c = Roo.get(els.close, true);
37827 c.dom.title = this.closeText;
37828 c.addClassOnOver("close-over");
37829 c.on("click", this.closeClick, this);
37835 * Fires when this tab becomes the active tab.
37836 * @param {Roo.TabPanel} tabPanel The parent TabPanel
37837 * @param {Roo.TabPanelItem} this
37841 * @event beforeclose
37842 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
37843 * @param {Roo.TabPanelItem} this
37844 * @param {Object} e Set cancel to true on this object to cancel the close.
37846 "beforeclose": true,
37849 * Fires when this tab is closed.
37850 * @param {Roo.TabPanelItem} this
37854 * @event deactivate
37855 * Fires when this tab is no longer the active tab.
37856 * @param {Roo.TabPanel} tabPanel The parent TabPanel
37857 * @param {Roo.TabPanelItem} this
37859 "deactivate" : true
37861 this.hidden = false;
37863 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
37866 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
37868 purgeListeners : function(){
37869 Roo.util.Observable.prototype.purgeListeners.call(this);
37870 this.el.removeAllListeners();
37873 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
37876 this.pnode.addClass("active");
37879 this.tabPanel.stripWrap.repaint();
37881 this.fireEvent("activate", this.tabPanel, this);
37885 * Returns true if this tab is the active tab.
37886 * @return {Boolean}
37888 isActive : function(){
37889 return this.tabPanel.getActiveTab() == this;
37893 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
37896 this.pnode.removeClass("active");
37898 this.fireEvent("deactivate", this.tabPanel, this);
37901 hideAction : function(){
37902 this.bodyEl.hide();
37903 this.bodyEl.setStyle("position", "absolute");
37904 this.bodyEl.setLeft("-20000px");
37905 this.bodyEl.setTop("-20000px");
37908 showAction : function(){
37909 this.bodyEl.setStyle("position", "relative");
37910 this.bodyEl.setTop("");
37911 this.bodyEl.setLeft("");
37912 this.bodyEl.show();
37916 * Set the tooltip for the tab.
37917 * @param {String} tooltip The tab's tooltip
37919 setTooltip : function(text){
37920 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
37921 this.textEl.dom.qtip = text;
37922 this.textEl.dom.removeAttribute('title');
37924 this.textEl.dom.title = text;
37928 onTabClick : function(e){
37929 e.preventDefault();
37930 this.tabPanel.activate(this.id);
37933 onTabMouseDown : function(e){
37934 e.preventDefault();
37935 this.tabPanel.activate(this.id);
37938 getWidth : function(){
37939 return this.inner.getWidth();
37942 setWidth : function(width){
37943 var iwidth = width - this.pnode.getPadding("lr");
37944 this.inner.setWidth(iwidth);
37945 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
37946 this.pnode.setWidth(width);
37950 * Show or hide the tab
37951 * @param {Boolean} hidden True to hide or false to show.
37953 setHidden : function(hidden){
37954 this.hidden = hidden;
37955 this.pnode.setStyle("display", hidden ? "none" : "");
37959 * Returns true if this tab is "hidden"
37960 * @return {Boolean}
37962 isHidden : function(){
37963 return this.hidden;
37967 * Returns the text for this tab
37970 getText : function(){
37974 autoSize : function(){
37975 //this.el.beginMeasure();
37976 this.textEl.setWidth(1);
37978 * #2804 [new] Tabs in Roojs
37979 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
37981 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
37982 //this.el.endMeasure();
37986 * Sets the text for the tab (Note: this also sets the tooltip text)
37987 * @param {String} text The tab's text and tooltip
37989 setText : function(text){
37991 this.textEl.update(text);
37992 this.setTooltip(text);
37993 //if(!this.tabPanel.resizeTabs){
37994 // this.autoSize();
37998 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38000 activate : function(){
38001 this.tabPanel.activate(this.id);
38005 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38007 disable : function(){
38008 if(this.tabPanel.active != this){
38009 this.disabled = true;
38010 this.pnode.addClass("disabled");
38015 * Enables this TabPanelItem if it was previously disabled.
38017 enable : function(){
38018 this.disabled = false;
38019 this.pnode.removeClass("disabled");
38023 * Sets the content for this TabPanelItem.
38024 * @param {String} content The content
38025 * @param {Boolean} loadScripts true to look for and load scripts
38027 setContent : function(content, loadScripts){
38028 this.bodyEl.update(content, loadScripts);
38032 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38033 * @return {Roo.UpdateManager} The UpdateManager
38035 getUpdateManager : function(){
38036 return this.bodyEl.getUpdateManager();
38040 * Set a URL to be used to load the content for this TabPanelItem.
38041 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38042 * @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)
38043 * @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)
38044 * @return {Roo.UpdateManager} The UpdateManager
38046 setUrl : function(url, params, loadOnce){
38047 if(this.refreshDelegate){
38048 this.un('activate', this.refreshDelegate);
38050 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38051 this.on("activate", this.refreshDelegate);
38052 return this.bodyEl.getUpdateManager();
38056 _handleRefresh : function(url, params, loadOnce){
38057 if(!loadOnce || !this.loaded){
38058 var updater = this.bodyEl.getUpdateManager();
38059 updater.update(url, params, this._setLoaded.createDelegate(this));
38064 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
38065 * Will fail silently if the setUrl method has not been called.
38066 * This does not activate the panel, just updates its content.
38068 refresh : function(){
38069 if(this.refreshDelegate){
38070 this.loaded = false;
38071 this.refreshDelegate();
38076 _setLoaded : function(){
38077 this.loaded = true;
38081 closeClick : function(e){
38084 this.fireEvent("beforeclose", this, o);
38085 if(o.cancel !== true){
38086 this.tabPanel.removeTab(this.id);
38090 * The text displayed in the tooltip for the close icon.
38093 closeText : "Close this tab"
38096 * This script refer to:
38097 * Title: International Telephone Input
38098 * Author: Jack O'Connor
38099 * Code version: v12.1.12
38100 * Availability: https://github.com/jackocnr/intl-tel-input.git
38103 Roo.bootstrap.PhoneInputData = function() {
38106 "Afghanistan (افغانستان)",
38111 "Albania (Shqipëri)",
38116 "Algeria (الجزائر)",
38141 "Antigua and Barbuda",
38151 "Armenia (Հայաստան)",
38167 "Austria (Österreich)",
38172 "Azerbaijan (Azərbaycan)",
38182 "Bahrain (البحرين)",
38187 "Bangladesh (বাংলাদেশ)",
38197 "Belarus (Беларусь)",
38202 "Belgium (België)",
38232 "Bosnia and Herzegovina (Босна и Херцеговина)",
38247 "British Indian Ocean Territory",
38252 "British Virgin Islands",
38262 "Bulgaria (България)",
38272 "Burundi (Uburundi)",
38277 "Cambodia (កម្ពុជា)",
38282 "Cameroon (Cameroun)",
38291 ["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"]
38294 "Cape Verde (Kabu Verdi)",
38299 "Caribbean Netherlands",
38310 "Central African Republic (République centrafricaine)",
38330 "Christmas Island",
38336 "Cocos (Keeling) Islands",
38347 "Comoros (جزر القمر)",
38352 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38357 "Congo (Republic) (Congo-Brazzaville)",
38377 "Croatia (Hrvatska)",
38398 "Czech Republic (Česká republika)",
38403 "Denmark (Danmark)",
38418 "Dominican Republic (República Dominicana)",
38422 ["809", "829", "849"]
38440 "Equatorial Guinea (Guinea Ecuatorial)",
38460 "Falkland Islands (Islas Malvinas)",
38465 "Faroe Islands (Føroyar)",
38486 "French Guiana (Guyane française)",
38491 "French Polynesia (Polynésie française)",
38506 "Georgia (საქართველო)",
38511 "Germany (Deutschland)",
38531 "Greenland (Kalaallit Nunaat)",
38568 "Guinea-Bissau (Guiné Bissau)",
38593 "Hungary (Magyarország)",
38598 "Iceland (Ísland)",
38618 "Iraq (العراق)",
38634 "Israel (ישראל)",
38661 "Jordan (الأردن)",
38666 "Kazakhstan (Казахстан)",
38687 "Kuwait (الكويت)",
38692 "Kyrgyzstan (Кыргызстан)",
38702 "Latvia (Latvija)",
38707 "Lebanon (لبنان)",
38722 "Libya (ليبيا)",
38732 "Lithuania (Lietuva)",
38747 "Macedonia (FYROM) (Македонија)",
38752 "Madagascar (Madagasikara)",
38782 "Marshall Islands",
38792 "Mauritania (موريتانيا)",
38797 "Mauritius (Moris)",
38818 "Moldova (Republica Moldova)",
38828 "Mongolia (Монгол)",
38833 "Montenegro (Crna Gora)",
38843 "Morocco (المغرب)",
38849 "Mozambique (Moçambique)",
38854 "Myanmar (Burma) (မြန်မာ)",
38859 "Namibia (Namibië)",
38874 "Netherlands (Nederland)",
38879 "New Caledonia (Nouvelle-Calédonie)",
38914 "North Korea (조선 민주주의 인민 공화국)",
38919 "Northern Mariana Islands",
38935 "Pakistan (پاکستان)",
38945 "Palestine (فلسطين)",
38955 "Papua New Guinea",
38997 "Réunion (La Réunion)",
39003 "Romania (România)",
39019 "Saint Barthélemy",
39030 "Saint Kitts and Nevis",
39040 "Saint Martin (Saint-Martin (partie française))",
39046 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39051 "Saint Vincent and the Grenadines",
39066 "São Tomé and Príncipe (São Tomé e Príncipe)",
39071 "Saudi Arabia (المملكة العربية السعودية)",
39076 "Senegal (Sénégal)",
39106 "Slovakia (Slovensko)",
39111 "Slovenia (Slovenija)",
39121 "Somalia (Soomaaliya)",
39131 "South Korea (대한민국)",
39136 "South Sudan (جنوب السودان)",
39146 "Sri Lanka (ශ්රී ලංකාව)",
39151 "Sudan (السودان)",
39161 "Svalbard and Jan Mayen",
39172 "Sweden (Sverige)",
39177 "Switzerland (Schweiz)",
39182 "Syria (سوريا)",
39227 "Trinidad and Tobago",
39232 "Tunisia (تونس)",
39237 "Turkey (Türkiye)",
39247 "Turks and Caicos Islands",
39257 "U.S. Virgin Islands",
39267 "Ukraine (Україна)",
39272 "United Arab Emirates (الإمارات العربية المتحدة)",
39294 "Uzbekistan (Oʻzbekiston)",
39304 "Vatican City (Città del Vaticano)",
39315 "Vietnam (Việt Nam)",
39320 "Wallis and Futuna (Wallis-et-Futuna)",
39325 "Western Sahara (الصحراء الغربية)",
39331 "Yemen (اليمن)",
39355 * This script refer to:
39356 * Title: International Telephone Input
39357 * Author: Jack O'Connor
39358 * Code version: v12.1.12
39359 * Availability: https://github.com/jackocnr/intl-tel-input.git
39363 * @class Roo.bootstrap.PhoneInput
39364 * @extends Roo.bootstrap.TriggerField
39365 * An input with International dial-code selection
39367 * @cfg {String} defaultDialCode default '+852'
39368 * @cfg {Array} preferedCountries default []
39371 * Create a new PhoneInput.
39372 * @param {Object} config Configuration options
39375 Roo.bootstrap.PhoneInput = function(config) {
39376 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39379 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39381 listWidth: undefined,
39383 selectedClass: 'active',
39385 invalidClass : "has-warning",
39387 validClass: 'has-success',
39389 allowed: '0123456789',
39392 * @cfg {String} defaultDialCode The default dial code when initializing the input
39394 defaultDialCode: '+852',
39397 * @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
39399 preferedCountries: false,
39401 getAutoCreate : function()
39403 var data = Roo.bootstrap.PhoneInputData();
39404 var align = this.labelAlign || this.parentLabelAlign();
39407 this.allCountries = [];
39408 this.dialCodeMapping = [];
39410 for (var i = 0; i < data.length; i++) {
39412 this.allCountries[i] = {
39416 priority: c[3] || 0,
39417 areaCodes: c[4] || null
39419 this.dialCodeMapping[c[2]] = {
39422 priority: c[3] || 0,
39423 areaCodes: c[4] || null
39435 cls : 'form-control tel-input',
39436 autocomplete: 'new-password'
39439 var hiddenInput = {
39442 cls: 'hidden-tel-input'
39446 hiddenInput.name = this.name;
39449 if (this.disabled) {
39450 input.disabled = true;
39453 var flag_container = {
39470 cls: this.hasFeedback ? 'has-feedback' : '',
39476 cls: 'dial-code-holder',
39483 cls: 'roo-select2-container input-group',
39490 if (this.fieldLabel.length) {
39493 tooltip: 'This field is required'
39499 cls: 'control-label',
39505 html: this.fieldLabel
39508 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39514 if(this.indicatorpos == 'right') {
39515 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39522 if(align == 'left') {
39530 if(this.labelWidth > 12){
39531 label.style = "width: " + this.labelWidth + 'px';
39533 if(this.labelWidth < 13 && this.labelmd == 0){
39534 this.labelmd = this.labelWidth;
39536 if(this.labellg > 0){
39537 label.cls += ' col-lg-' + this.labellg;
39538 input.cls += ' col-lg-' + (12 - this.labellg);
39540 if(this.labelmd > 0){
39541 label.cls += ' col-md-' + this.labelmd;
39542 container.cls += ' col-md-' + (12 - this.labelmd);
39544 if(this.labelsm > 0){
39545 label.cls += ' col-sm-' + this.labelsm;
39546 container.cls += ' col-sm-' + (12 - this.labelsm);
39548 if(this.labelxs > 0){
39549 label.cls += ' col-xs-' + this.labelxs;
39550 container.cls += ' col-xs-' + (12 - this.labelxs);
39560 var settings = this;
39562 ['xs','sm','md','lg'].map(function(size){
39563 if (settings[size]) {
39564 cfg.cls += ' col-' + size + '-' + settings[size];
39568 this.store = new Roo.data.Store({
39569 proxy : new Roo.data.MemoryProxy({}),
39570 reader : new Roo.data.JsonReader({
39581 'name' : 'dialCode',
39585 'name' : 'priority',
39589 'name' : 'areaCodes',
39596 if(!this.preferedCountries) {
39597 this.preferedCountries = [
39604 var p = this.preferedCountries.reverse();
39607 for (var i = 0; i < p.length; i++) {
39608 for (var j = 0; j < this.allCountries.length; j++) {
39609 if(this.allCountries[j].iso2 == p[i]) {
39610 var t = this.allCountries[j];
39611 this.allCountries.splice(j,1);
39612 this.allCountries.unshift(t);
39618 this.store.proxy.data = {
39620 data: this.allCountries
39626 initEvents : function()
39629 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
39631 this.indicator = this.indicatorEl();
39632 this.flag = this.flagEl();
39633 this.dialCodeHolder = this.dialCodeHolderEl();
39635 this.trigger = this.el.select('div.flag-box',true).first();
39636 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39641 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
39642 _this.list.setWidth(lw);
39645 this.list.on('mouseover', this.onViewOver, this);
39646 this.list.on('mousemove', this.onViewMove, this);
39647 this.inputEl().on("keyup", this.onKeyUp, this);
39649 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
39651 this.view = new Roo.View(this.list, this.tpl, {
39652 singleSelect:true, store: this.store, selectedClass: this.selectedClass
39655 this.view.on('click', this.onViewClick, this);
39656 this.setValue(this.defaultDialCode);
39659 onTriggerClick : function(e)
39661 Roo.log('trigger click');
39666 if(this.isExpanded()){
39668 this.hasFocus = false;
39670 this.store.load({});
39671 this.hasFocus = true;
39676 isExpanded : function()
39678 return this.list.isVisible();
39681 collapse : function()
39683 if(!this.isExpanded()){
39687 Roo.get(document).un('mousedown', this.collapseIf, this);
39688 Roo.get(document).un('mousewheel', this.collapseIf, this);
39689 this.fireEvent('collapse', this);
39693 expand : function()
39697 if(this.isExpanded() || !this.hasFocus){
39701 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
39702 this.list.setWidth(lw);
39705 this.restrictHeight();
39707 Roo.get(document).on('mousedown', this.collapseIf, this);
39708 Roo.get(document).on('mousewheel', this.collapseIf, this);
39710 this.fireEvent('expand', this);
39713 restrictHeight : function()
39715 this.list.alignTo(this.inputEl(), this.listAlign);
39716 this.list.alignTo(this.inputEl(), this.listAlign);
39719 onViewOver : function(e, t)
39721 if(this.inKeyMode){
39724 var item = this.view.findItemFromChild(t);
39727 var index = this.view.indexOf(item);
39728 this.select(index, false);
39733 onViewClick : function(view, doFocus, el, e)
39735 var index = this.view.getSelectedIndexes()[0];
39737 var r = this.store.getAt(index);
39740 this.onSelect(r, index);
39742 if(doFocus !== false && !this.blockFocus){
39743 this.inputEl().focus();
39747 onViewMove : function(e, t)
39749 this.inKeyMode = false;
39752 select : function(index, scrollIntoView)
39754 this.selectedIndex = index;
39755 this.view.select(index);
39756 if(scrollIntoView !== false){
39757 var el = this.view.getNode(index);
39759 this.list.scrollChildIntoView(el, false);
39764 createList : function()
39766 this.list = Roo.get(document.body).createChild({
39768 cls: 'typeahead typeahead-long dropdown-menu tel-list',
39769 style: 'display:none'
39771 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
39774 collapseIf : function(e)
39776 var in_combo = e.within(this.el);
39777 var in_list = e.within(this.list);
39778 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
39780 if (in_combo || in_list || is_list) {
39786 onSelect : function(record, index)
39788 if(this.fireEvent('beforeselect', this, record, index) !== false){
39790 this.setFlagClass(record.data.iso2);
39791 this.setDialCode(record.data.dialCode);
39792 this.hasFocus = false;
39794 this.fireEvent('select', this, record, index);
39798 flagEl : function()
39800 var flag = this.el.select('div.flag',true).first();
39807 dialCodeHolderEl : function()
39809 var d = this.el.select('input.dial-code-holder',true).first();
39816 setDialCode : function(v)
39818 this.dialCodeHolder.dom.value = '+'+v;
39821 setFlagClass : function(n)
39823 this.flag.dom.className = 'flag '+n;
39826 getValue : function()
39828 var v = this.inputEl().getValue();
39829 if(this.dialCodeHolder) {
39830 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
39835 setValue : function(v)
39837 var d = this.getDialCode(v);
39839 //invalid dial code
39840 if(v.length == 0 || !d || d.length == 0) {
39842 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
39843 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
39849 this.setFlagClass(this.dialCodeMapping[d].iso2);
39850 this.setDialCode(d);
39851 this.inputEl().dom.value = v.replace('+'+d,'');
39852 this.hiddenEl().dom.value = this.getValue();
39857 getDialCode : function(v = '')
39859 if (v.length == 0) {
39860 return this.dialCodeHolder.dom.value;
39864 if (v.charAt(0) != "+") {
39867 var numericChars = "";
39868 for (var i = 1; i < v.length; i++) {
39869 var c = v.charAt(i);
39872 if (this.dialCodeMapping[numericChars]) {
39873 dialCode = v.substr(1, i);
39875 if (numericChars.length == 4) {
39885 this.setValue(this.defaultDialCode);
39889 hiddenEl : function()
39891 return this.el.select('input.hidden-tel-input',true).first();
39894 onKeyUp : function(e){
39896 var k = e.getKey();
39897 var c = e.getCharCode();
39900 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
39901 this.allowed.indexOf(String.fromCharCode(c)) === -1
39906 // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
39909 if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
39913 this.setValue(this.getValue());
39918 * @class Roo.bootstrap.MoneyField
39919 * @extends Roo.bootstrap.ComboBox
39920 * Bootstrap MoneyField class
39923 * Create a new MoneyField.
39924 * @param {Object} config Configuration options
39927 Roo.bootstrap.MoneyField = function(config) {
39929 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
39933 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
39936 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
39938 allowDecimals : true,
39940 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
39942 decimalSeparator : ".",
39944 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
39946 decimalPrecision : 2,
39948 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
39950 allowNegative : true,
39952 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
39954 minValue : Number.NEGATIVE_INFINITY,
39956 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
39958 maxValue : Number.MAX_VALUE,
39960 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
39962 minText : "The minimum value for this field is {0}",
39964 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
39966 maxText : "The maximum value for this field is {0}",
39968 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
39969 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
39971 nanText : "{0} is not a valid number",
39973 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
39977 * @cfg {String} defaults currency of the MoneyField
39978 * value should be in lkey
39980 defaultCurrency : false,
39990 getAutoCreate : function()
39992 var align = this.labelAlign || this.parentLabelAlign();
40004 cls : 'form-control roo-money-amount-input',
40005 autocomplete: 'new-password'
40009 input.name = this.name;
40012 if (this.disabled) {
40013 input.disabled = true;
40016 var clg = 12 - this.inputlg;
40017 var cmd = 12 - this.inputmd;
40018 var csm = 12 - this.inputsm;
40019 var cxs = 12 - this.inputxs;
40023 cls : 'row roo-money-field',
40027 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40031 cls: 'roo-select2-container input-group',
40035 cls : 'form-control roo-money-currency-input',
40036 autocomplete: 'new-password',
40038 name : this.currencyName
40042 cls : 'input-group-addon',
40056 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40060 cls: this.hasFeedback ? 'has-feedback' : '',
40071 if (this.fieldLabel.length) {
40074 tooltip: 'This field is required'
40080 cls: 'control-label',
40086 html: this.fieldLabel
40089 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40095 if(this.indicatorpos == 'right') {
40096 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40103 if(align == 'left') {
40111 if(this.labelWidth > 12){
40112 label.style = "width: " + this.labelWidth + 'px';
40114 if(this.labelWidth < 13 && this.labelmd == 0){
40115 this.labelmd = this.labelWidth;
40117 if(this.labellg > 0){
40118 label.cls += ' col-lg-' + this.labellg;
40119 input.cls += ' col-lg-' + (12 - this.labellg);
40121 if(this.labelmd > 0){
40122 label.cls += ' col-md-' + this.labelmd;
40123 container.cls += ' col-md-' + (12 - this.labelmd);
40125 if(this.labelsm > 0){
40126 label.cls += ' col-sm-' + this.labelsm;
40127 container.cls += ' col-sm-' + (12 - this.labelsm);
40129 if(this.labelxs > 0){
40130 label.cls += ' col-xs-' + this.labelxs;
40131 container.cls += ' col-xs-' + (12 - this.labelxs);
40141 var settings = this;
40143 ['xs','sm','md','lg'].map(function(size){
40144 if (settings[size]) {
40145 cfg.cls += ' col-' + size + '-' + settings[size];
40153 initEvents : function()
40155 this.indicator = this.indicatorEl();
40157 this.initCurrencyEvent();
40159 this.initNumberEvent();
40163 initCurrencyEvent : function()
40166 throw "can not find store for combo";
40169 this.store = Roo.factory(this.store, Roo.data);
40170 this.store.parent = this;
40174 this.triggerEl = this.el.select('.input-group-addon', true).first();
40176 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40181 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40182 _this.list.setWidth(lw);
40185 this.list.on('mouseover', this.onViewOver, this);
40186 this.list.on('mousemove', this.onViewMove, this);
40187 this.list.on('scroll', this.onViewScroll, this);
40190 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40193 this.view = new Roo.View(this.list, this.tpl, {
40194 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40197 this.view.on('click', this.onViewClick, this);
40199 this.store.on('beforeload', this.onBeforeLoad, this);
40200 this.store.on('load', this.onLoad, this);
40201 this.store.on('loadexception', this.onLoadException, this);
40203 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40204 "up" : function(e){
40205 this.inKeyMode = true;
40209 "down" : function(e){
40210 if(!this.isExpanded()){
40211 this.onTriggerClick();
40213 this.inKeyMode = true;
40218 "enter" : function(e){
40221 if(this.fireEvent("specialkey", this, e)){
40222 this.onViewClick(false);
40228 "esc" : function(e){
40232 "tab" : function(e){
40235 if(this.fireEvent("specialkey", this, e)){
40236 this.onViewClick(false);
40244 doRelay : function(foo, bar, hname){
40245 if(hname == 'down' || this.scope.isExpanded()){
40246 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40254 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40258 initNumberEvent : function(e)
40260 this.inputEl().on("keydown" , this.fireKey, this);
40261 this.inputEl().on("focus", this.onFocus, this);
40262 this.inputEl().on("blur", this.onBlur, this);
40264 this.inputEl().relayEvent('keyup', this);
40266 if(this.indicator){
40267 this.indicator.addClass('invisible');
40270 this.originalValue = this.getValue();
40272 if(this.validationEvent == 'keyup'){
40273 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40274 this.inputEl().on('keyup', this.filterValidation, this);
40276 else if(this.validationEvent !== false){
40277 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40280 if(this.selectOnFocus){
40281 this.on("focus", this.preFocus, this);
40284 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40285 this.inputEl().on("keypress", this.filterKeys, this);
40287 this.inputEl().relayEvent('keypress', this);
40290 var allowed = "0123456789";
40292 if(this.allowDecimals){
40293 allowed += this.decimalSeparator;
40296 if(this.allowNegative){
40300 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40302 var keyPress = function(e){
40304 var k = e.getKey();
40306 var c = e.getCharCode();
40309 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40310 allowed.indexOf(String.fromCharCode(c)) === -1
40316 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40320 if(allowed.indexOf(String.fromCharCode(c)) === -1){
40325 this.inputEl().on("keypress", keyPress, this);
40329 onTriggerClick : function(e)
40336 this.loadNext = false;
40338 if(this.isExpanded()){
40343 this.hasFocus = true;
40345 if(this.triggerAction == 'all') {
40346 this.doQuery(this.allQuery, true);
40350 this.doQuery(this.getRawValue());
40353 getCurrency : function()
40355 var v = this.currencyEl().getValue();
40360 restrictHeight : function()
40362 this.list.alignTo(this.currencyEl(), this.listAlign);
40363 this.list.alignTo(this.currencyEl(), this.listAlign);
40366 onViewClick : function(view, doFocus, el, e)
40368 var index = this.view.getSelectedIndexes()[0];
40370 var r = this.store.getAt(index);
40373 this.onSelect(r, index);
40377 onSelect : function(record, index){
40379 if(this.fireEvent('beforeselect', this, record, index) !== false){
40381 this.setFromCurrencyData(index > -1 ? record.data : false);
40385 this.fireEvent('select', this, record, index);
40389 setFromCurrencyData : function(o)
40393 this.lastCurrency = o;
40395 if (this.currencyField) {
40396 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40398 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
40401 this.lastSelectionText = currency;
40403 //setting default currency
40404 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40405 this.setCurrency(this.defaultCurrency);
40409 this.setCurrency(currency);
40412 setFromData : function(o)
40416 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40418 this.setFromCurrencyData(c);
40423 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40425 Roo.log('no value set for '+ (this.name ? this.name : this.id));
40428 this.setValue(value);
40432 setCurrency : function(v)
40434 this.currencyValue = v;
40437 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40442 setValue : function(v)
40444 v = this.fixPrecision(v);
40446 v = String(v).replace(".", this.decimalSeparator);
40451 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40456 getRawValue : function()
40458 var v = this.inputEl().getValue();
40463 getValue : function()
40465 return this.fixPrecision(this.parseValue(this.getRawValue()));
40468 parseValue : function(value)
40470 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40471 return isNaN(value) ? '' : value;
40474 fixPrecision : function(value)
40476 var nan = isNaN(value);
40478 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40479 return nan ? '' : value;
40482 return parseFloat(value).toFixed(this.decimalPrecision);
40485 decimalPrecisionFcn : function(v)
40487 return Math.floor(v);
40490 validateValue : function(value)
40492 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40496 var num = this.parseValue(value);
40499 this.markInvalid(String.format(this.nanText, value));
40503 if(num < this.minValue){
40504 this.markInvalid(String.format(this.minText, this.minValue));
40508 if(num > this.maxValue){
40509 this.markInvalid(String.format(this.maxText, this.maxValue));
40516 validate : function()
40518 if(this.disabled || this.allowBlank){
40523 var currency = this.getCurrency();
40525 if(this.validateValue(this.getRawValue()) && currency.length){
40530 this.markInvalid();
40534 getName: function()
40539 beforeBlur : function()
40545 var v = this.parseValue(this.getRawValue());
40552 onBlur : function()
40556 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40557 //this.el.removeClass(this.focusClass);
40560 this.hasFocus = false;
40562 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40566 var v = this.getValue();
40568 if(String(v) !== String(this.startValue)){
40569 this.fireEvent('change', this, v, this.startValue);
40572 this.fireEvent("blur", this);
40575 inputEl : function()
40577 return this.el.select('.roo-money-amount-input', true).first();
40580 currencyEl : function()
40582 return this.el.select('.roo-money-currency-input', true).first();