4 * base class for bootstrap elements.
8 Roo.bootstrap = Roo.bootstrap || {};
10 * @class Roo.bootstrap.Component
11 * @extends Roo.Component
12 * Bootstrap Component base class
13 * @cfg {String} cls css class
14 * @cfg {String} style any extra css
15 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
17 * @cfg {string} dataId cutomer id
18 * @cfg {string} name Specifies name attribute
19 * @cfg {string} tooltip Text for the tooltip
20 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
23 * Do not use directly - it does not do anything..
24 * @param {Object} config The config object
29 Roo.bootstrap.Component = function(config){
30 Roo.bootstrap.Component.superclass.constructor.call(this, config);
34 * @event childrenrendered
35 * Fires when the children have been rendered..
36 * @param {Roo.bootstrap.Component} this
38 "childrenrendered" : true
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
50 allowDomMove : false, // to stop relocations in parent onRender...
60 * Initialize Events for the element
62 initEvents : function() { },
68 can_build_overlaid : true,
70 container_method : false,
77 // returns the parent component..
78 return Roo.ComponentMgr.get(this.parentId)
84 onRender : function(ct, position)
86 // Roo.log("Call onRender: " + this.xtype);
88 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
91 if (this.el.attr('xtype')) {
92 this.el.attr('xtypex', this.el.attr('xtype'));
93 this.el.dom.removeAttribute('xtype');
103 var cfg = Roo.apply({}, this.getAutoCreate());
105 cfg.id = this.id || Roo.id();
107 // fill in the extra attributes
108 if (this.xattr && typeof(this.xattr) =='object') {
109 for (var i in this.xattr) {
110 cfg[i] = this.xattr[i];
115 cfg.dataId = this.dataId;
119 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
122 if (this.style) { // fixme needs to support more complex style data.
123 cfg.style = this.style;
127 cfg.name = this.name;
130 this.el = ct.createChild(cfg, position);
133 this.tooltipEl().attr('tooltip', this.tooltip);
136 if(this.tabIndex !== undefined){
137 this.el.dom.setAttribute('tabIndex', this.tabIndex);
144 * Fetch the element to add children to
145 * @return {Roo.Element} defaults to this.el
147 getChildContainer : function()
152 * Fetch the element to display the tooltip on.
153 * @return {Roo.Element} defaults to this.el
155 tooltipEl : function()
160 addxtype : function(tree,cntr)
164 cn = Roo.factory(tree);
165 //Roo.log(['addxtype', cn]);
167 cn.parentType = this.xtype; //??
168 cn.parentId = this.id;
170 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
171 if (typeof(cn.container_method) == 'string') {
172 cntr = cn.container_method;
176 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
178 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
180 var build_from_html = Roo.XComponent.build_from_html;
182 var is_body = (tree.xtype == 'Body') ;
184 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
186 var self_cntr_el = Roo.get(this[cntr](false));
188 // do not try and build conditional elements
189 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
193 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
194 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
195 return this.addxtypeChild(tree,cntr, is_body);
198 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
201 return this.addxtypeChild(Roo.apply({}, tree),cntr);
204 Roo.log('skipping render');
210 if (!build_from_html) {
214 // this i think handles overlaying multiple children of the same type
215 // with the sam eelement.. - which might be buggy..
217 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
223 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
227 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
234 addxtypeChild : function (tree, cntr, is_body)
236 Roo.debug && Roo.log('addxtypeChild:' + cntr);
238 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
241 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
242 (typeof(tree['flexy:foreach']) != 'undefined');
246 skip_children = false;
247 // render the element if it's not BODY.
250 cn = Roo.factory(tree);
252 cn.parentType = this.xtype; //??
253 cn.parentId = this.id;
255 var build_from_html = Roo.XComponent.build_from_html;
258 // does the container contain child eleemnts with 'xtype' attributes.
259 // that match this xtype..
260 // note - when we render we create these as well..
261 // so we should check to see if body has xtype set.
262 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
264 var self_cntr_el = Roo.get(this[cntr](false));
265 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
267 //Roo.log(Roo.XComponent.build_from_html);
268 //Roo.log("got echild:");
271 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
272 // and are not displayed -this causes this to use up the wrong element when matching.
273 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
276 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
277 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
283 //echild.dom.removeAttribute('xtype');
285 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
286 Roo.debug && Roo.log(self_cntr_el);
287 Roo.debug && Roo.log(echild);
288 Roo.debug && Roo.log(cn);
294 // if object has flexy:if - then it may or may not be rendered.
295 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
296 // skip a flexy if element.
297 Roo.debug && Roo.log('skipping render');
298 Roo.debug && Roo.log(tree);
300 Roo.debug && Roo.log('skipping all children');
301 skip_children = true;
306 // actually if flexy:foreach is found, we really want to create
307 // multiple copies here...
309 //Roo.log(this[cntr]());
310 // some elements do not have render methods.. like the layouts...
311 cn.render && cn.render(this[cntr](true));
313 // then add the element..
321 if (typeof (tree.menu) != 'undefined') {
322 tree.menu.parentType = cn.xtype;
323 tree.menu.triggerEl = cn.el;
324 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
328 if (!tree.items || !tree.items.length) {
330 //Roo.log(["no children", this]);
335 var items = tree.items;
338 //Roo.log(items.length);
340 if (!skip_children) {
341 for(var i =0;i < items.length;i++) {
342 // Roo.log(['add child', items[i]]);
343 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
349 //Roo.log("fire childrenrendered");
351 cn.fireEvent('childrenrendered', this);
356 * Show a component - removes 'hidden' class
361 this.el.removeClass('hidden');
365 * Hide a component - adds 'hidden' class
369 if (this.el && !this.el.hasClass('hidden')) {
370 this.el.addClass('hidden');
384 * @class Roo.bootstrap.Body
385 * @extends Roo.bootstrap.Component
386 * Bootstrap Body class
390 * @param {Object} config The config object
393 Roo.bootstrap.Body = function(config){
395 config = config || {};
397 Roo.bootstrap.Body.superclass.constructor.call(this, config);
398 this.el = Roo.get(config.el ? config.el : document.body );
399 if (this.cls && this.cls.length) {
400 Roo.get(document.body).addClass(this.cls);
404 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
406 is_body : true,// just to make sure it's constructed?
411 onRender : function(ct, position)
413 /* Roo.log("Roo.bootstrap.Body - onRender");
414 if (this.cls && this.cls.length) {
415 Roo.get(document.body).addClass(this.cls);
434 * @class Roo.bootstrap.ButtonGroup
435 * @extends Roo.bootstrap.Component
436 * Bootstrap ButtonGroup class
437 * @cfg {String} size lg | sm | xs (default empty normal)
438 * @cfg {String} align vertical | justified (default none)
439 * @cfg {String} direction up | down (default down)
440 * @cfg {Boolean} toolbar false | true
441 * @cfg {Boolean} btn true | false
446 * @param {Object} config The config object
449 Roo.bootstrap.ButtonGroup = function(config){
450 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
453 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
461 getAutoCreate : function(){
467 cfg.html = this.html || cfg.html;
478 if (['vertical','justified'].indexOf(this.align)!==-1) {
479 cfg.cls = 'btn-group-' + this.align;
481 if (this.align == 'justified') {
482 console.log(this.items);
486 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
487 cfg.cls += ' btn-group-' + this.size;
490 if (this.direction == 'up') {
491 cfg.cls += ' dropup' ;
507 * @class Roo.bootstrap.Button
508 * @extends Roo.bootstrap.Component
509 * Bootstrap Button class
510 * @cfg {String} html The button content
511 * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default
512 * @cfg {String} size ( lg | sm | xs)
513 * @cfg {String} tag ( a | input | submit)
514 * @cfg {String} href empty or href
515 * @cfg {Boolean} disabled default false;
516 * @cfg {Boolean} isClose default false;
517 * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
518 * @cfg {String} badge text for badge
519 * @cfg {String} theme default
520 * @cfg {Boolean} inverse
521 * @cfg {Boolean} toggle
522 * @cfg {String} ontext text for on toggle state
523 * @cfg {String} offtext text for off toggle state
524 * @cfg {Boolean} defaulton
525 * @cfg {Boolean} preventDefault default true
526 * @cfg {Boolean} removeClass remove the standard class..
527 * @cfg {String} target target for a href. (_self|_blank|_parent|_top| other)
530 * Create a new button
531 * @param {Object} config The config object
535 Roo.bootstrap.Button = function(config){
536 Roo.bootstrap.Button.superclass.constructor.call(this, config);
537 this.weightClass = ["btn-default",
549 * When a butotn is pressed
550 * @param {Roo.bootstrap.Button} this
551 * @param {Roo.EventObject} e
556 * After the button has been toggles
557 * @param {Roo.EventObject} e
558 * @param {boolean} pressed (also available as button.pressed)
564 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
582 preventDefault: true,
591 getAutoCreate : function(){
599 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
600 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
605 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
607 if (this.toggle == true) {
610 cls: 'slider-frame roo-button',
615 'data-off-text':'OFF',
616 cls: 'slider-button',
622 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
623 cfg.cls += ' '+this.weight;
632 cfg["aria-hidden"] = true;
634 cfg.html = "×";
640 if (this.theme==='default') {
641 cfg.cls = 'btn roo-button';
643 //if (this.parentType != 'Navbar') {
644 this.weight = this.weight.length ? this.weight : 'default';
646 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
648 cfg.cls += ' btn-' + this.weight;
650 } else if (this.theme==='glow') {
653 cfg.cls = 'btn-glow roo-button';
655 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
657 cfg.cls += ' ' + this.weight;
663 this.cls += ' inverse';
668 cfg.cls += ' active';
672 cfg.disabled = 'disabled';
676 Roo.log('changing to ul' );
678 this.glyphicon = 'caret';
681 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
683 //gsRoo.log(this.parentType);
684 if (this.parentType === 'Navbar' && !this.parent().bar) {
685 Roo.log('changing to li?');
694 href : this.href || '#'
697 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
698 cfg.cls += ' dropdown';
705 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
707 if (this.glyphicon) {
708 cfg.html = ' ' + cfg.html;
713 cls: 'glyphicon glyphicon-' + this.glyphicon
723 // cfg.cls='btn roo-button';
727 var value = cfg.html;
732 cls: 'glyphicon glyphicon-' + this.glyphicon,
751 cfg.cls += ' dropdown';
752 cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
755 if (cfg.tag !== 'a' && this.href !== '') {
756 throw "Tag must be a to set href.";
757 } else if (this.href.length > 0) {
758 cfg.href = this.href;
761 if(this.removeClass){
766 cfg.target = this.target;
771 initEvents: function() {
772 // Roo.log('init events?');
773 // Roo.log(this.el.dom);
776 if (typeof (this.menu) != 'undefined') {
777 this.menu.parentType = this.xtype;
778 this.menu.triggerEl = this.el;
779 this.addxtype(Roo.apply({}, this.menu));
783 if (this.el.hasClass('roo-button')) {
784 this.el.on('click', this.onClick, this);
786 this.el.select('.roo-button').on('click', this.onClick, this);
789 if(this.removeClass){
790 this.el.on('click', this.onClick, this);
793 this.el.enableDisplayMode();
796 onClick : function(e)
803 Roo.log('button on click ');
804 if(this.preventDefault){
807 if (this.pressed === true || this.pressed === false) {
808 this.pressed = !this.pressed;
809 this.el[this.pressed ? 'addClass' : 'removeClass']('active');
810 this.fireEvent('toggle', this, e, this.pressed);
814 this.fireEvent('click', this, e);
818 * Enables this button
822 this.disabled = false;
823 this.el.removeClass('disabled');
827 * Disable this button
831 this.disabled = true;
832 this.el.addClass('disabled');
835 * sets the active state on/off,
836 * @param {Boolean} state (optional) Force a particular state
838 setActive : function(v) {
840 this.el[v ? 'addClass' : 'removeClass']('active');
843 * toggles the current active state
845 toggleActive : function()
847 var active = this.el.hasClass('active');
848 this.setActive(!active);
852 setText : function(str)
854 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
858 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
869 setWeight : function(str)
871 this.el.removeClass(this.weightClass);
872 this.el.addClass('btn-' + str);
886 * @class Roo.bootstrap.Column
887 * @extends Roo.bootstrap.Component
888 * Bootstrap Column class
889 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
890 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
891 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
892 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
893 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
894 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
895 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
896 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
899 * @cfg {Boolean} hidden (true|false) hide the element
900 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
901 * @cfg {String} fa (ban|check|...) font awesome icon
902 * @cfg {Number} fasize (1|2|....) font awsome size
904 * @cfg {String} icon (info-sign|check|...) glyphicon name
906 * @cfg {String} html content of column.
909 * Create a new Column
910 * @param {Object} config The config object
913 Roo.bootstrap.Column = function(config){
914 Roo.bootstrap.Column.superclass.constructor.call(this, config);
917 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
935 getAutoCreate : function(){
936 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
944 ['xs','sm','md','lg'].map(function(size){
945 //Roo.log( size + ':' + settings[size]);
947 if (settings[size+'off'] !== false) {
948 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
951 if (settings[size] === false) {
955 if (!settings[size]) { // 0 = hidden
956 cfg.cls += ' hidden-' + size;
959 cfg.cls += ' col-' + size + '-' + settings[size];
964 cfg.cls += ' hidden';
967 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
968 cfg.cls +=' alert alert-' + this.alert;
972 if (this.html.length) {
973 cfg.html = this.html;
977 if (this.fasize > 1) {
978 fasize = ' fa-' + this.fasize + 'x';
980 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
985 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1004 * @class Roo.bootstrap.Container
1005 * @extends Roo.bootstrap.Component
1006 * Bootstrap Container class
1007 * @cfg {Boolean} jumbotron is it a jumbotron element
1008 * @cfg {String} html content of element
1009 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1010 * @cfg {String} panel (primary|success|info|warning|danger) render as panel - type - primary/success.....
1011 * @cfg {String} header content of header (for panel)
1012 * @cfg {String} footer content of footer (for panel)
1013 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1014 * @cfg {String} tag (header|aside|section) type of HTML tag.
1015 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1016 * @cfg {String} fa font awesome icon
1017 * @cfg {String} icon (info-sign|check|...) glyphicon name
1018 * @cfg {Boolean} hidden (true|false) hide the element
1019 * @cfg {Boolean} expandable (true|false) default false
1020 * @cfg {Boolean} expanded (true|false) default true
1021 * @cfg {String} rheader contet on the right of header
1022 * @cfg {Boolean} clickable (true|false) default false
1026 * Create a new Container
1027 * @param {Object} config The config object
1030 Roo.bootstrap.Container = function(config){
1031 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1037 * After the panel has been expand
1039 * @param {Roo.bootstrap.Container} this
1044 * After the panel has been collapsed
1046 * @param {Roo.bootstrap.Container} this
1051 * When a element is chick
1052 * @param {Roo.bootstrap.Container} this
1053 * @param {Roo.EventObject} e
1059 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1077 getChildContainer : function() {
1083 if (this.panel.length) {
1084 return this.el.select('.panel-body',true).first();
1091 getAutoCreate : function(){
1094 tag : this.tag || 'div',
1098 if (this.jumbotron) {
1099 cfg.cls = 'jumbotron';
1104 // - this is applied by the parent..
1106 // cfg.cls = this.cls + '';
1109 if (this.sticky.length) {
1111 var bd = Roo.get(document.body);
1112 if (!bd.hasClass('bootstrap-sticky')) {
1113 bd.addClass('bootstrap-sticky');
1114 Roo.select('html',true).setStyle('height', '100%');
1117 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1121 if (this.well.length) {
1122 switch (this.well) {
1125 cfg.cls +=' well well-' +this.well;
1134 cfg.cls += ' hidden';
1138 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1139 cfg.cls +=' alert alert-' + this.alert;
1144 if (this.panel.length) {
1145 cfg.cls += ' panel panel-' + this.panel;
1147 if (this.header.length) {
1151 if(this.expandable){
1153 cfg.cls = cfg.cls + ' expandable';
1157 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1165 cls : 'panel-title',
1166 html : (this.expandable ? ' ' : '') + this.header
1170 cls: 'panel-header-right',
1176 cls : 'panel-heading',
1177 style : this.expandable ? 'cursor: pointer' : '',
1185 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1190 if (this.footer.length) {
1192 cls : 'panel-footer',
1201 body.html = this.html || cfg.html;
1202 // prefix with the icons..
1204 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1207 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1212 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1213 cfg.cls = 'container';
1219 initEvents: function()
1221 if(this.expandable){
1222 var headerEl = this.headerEl();
1225 headerEl.on('click', this.onToggleClick, this);
1230 this.el.on('click', this.onClick, this);
1235 onToggleClick : function()
1237 var headerEl = this.headerEl();
1253 if(this.fireEvent('expand', this)) {
1255 this.expanded = true;
1257 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1259 this.el.select('.panel-body',true).first().removeClass('hide');
1261 var toggleEl = this.toggleEl();
1267 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1272 collapse : function()
1274 if(this.fireEvent('collapse', this)) {
1276 this.expanded = false;
1278 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1279 this.el.select('.panel-body',true).first().addClass('hide');
1281 var toggleEl = this.toggleEl();
1287 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1291 toggleEl : function()
1293 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1297 return this.el.select('.panel-heading .fa',true).first();
1300 headerEl : function()
1302 if(!this.el || !this.panel.length || !this.header.length){
1306 return this.el.select('.panel-heading',true).first()
1311 if(!this.el || !this.panel.length){
1315 return this.el.select('.panel-body',true).first()
1318 titleEl : function()
1320 if(!this.el || !this.panel.length || !this.header.length){
1324 return this.el.select('.panel-title',true).first();
1327 setTitle : function(v)
1329 var titleEl = this.titleEl();
1335 titleEl.dom.innerHTML = v;
1338 getTitle : function()
1341 var titleEl = this.titleEl();
1347 return titleEl.dom.innerHTML;
1350 setRightTitle : function(v)
1352 var t = this.el.select('.panel-header-right',true).first();
1358 t.dom.innerHTML = v;
1361 onClick : function(e)
1365 this.fireEvent('click', this, e);
1379 * @class Roo.bootstrap.Img
1380 * @extends Roo.bootstrap.Component
1381 * Bootstrap Img class
1382 * @cfg {Boolean} imgResponsive false | true
1383 * @cfg {String} border rounded | circle | thumbnail
1384 * @cfg {String} src image source
1385 * @cfg {String} alt image alternative text
1386 * @cfg {String} href a tag href
1387 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1388 * @cfg {String} xsUrl xs image source
1389 * @cfg {String} smUrl sm image source
1390 * @cfg {String} mdUrl md image source
1391 * @cfg {String} lgUrl lg image source
1394 * Create a new Input
1395 * @param {Object} config The config object
1398 Roo.bootstrap.Img = function(config){
1399 Roo.bootstrap.Img.superclass.constructor.call(this, config);
1405 * The img click event for the img.
1406 * @param {Roo.EventObject} e
1412 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1414 imgResponsive: true,
1424 getAutoCreate : function()
1426 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1427 return this.createSingleImg();
1432 cls: 'roo-image-responsive-group',
1437 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1439 if(!_this[size + 'Url']){
1445 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1446 html: _this.html || cfg.html,
1447 src: _this[size + 'Url']
1450 img.cls += ' roo-image-responsive-' + size;
1452 var s = ['xs', 'sm', 'md', 'lg'];
1454 s.splice(s.indexOf(size), 1);
1456 Roo.each(s, function(ss){
1457 img.cls += ' hidden-' + ss;
1460 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1461 cfg.cls += ' img-' + _this.border;
1465 cfg.alt = _this.alt;
1478 a.target = _this.target;
1482 cfg.cn.push((_this.href) ? a : img);
1489 createSingleImg : function()
1493 cls: (this.imgResponsive) ? 'img-responsive' : '',
1495 src : 'about:blank' // just incase src get's set to undefined?!?
1498 cfg.html = this.html || cfg.html;
1500 cfg.src = this.src || cfg.src;
1502 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1503 cfg.cls += ' img-' + this.border;
1520 a.target = this.target;
1525 return (this.href) ? a : cfg;
1528 initEvents: function()
1531 this.el.on('click', this.onClick, this);
1536 onClick : function(e)
1538 Roo.log('img onclick');
1539 this.fireEvent('click', this, e);
1542 * Sets the url of the image - used to update it
1543 * @param {String} url the url of the image
1546 setSrc : function(url)
1550 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1551 this.el.dom.src = url;
1555 this.el.select('img', true).first().dom.src = url;
1571 * @class Roo.bootstrap.Link
1572 * @extends Roo.bootstrap.Component
1573 * Bootstrap Link Class
1574 * @cfg {String} alt image alternative text
1575 * @cfg {String} href a tag href
1576 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1577 * @cfg {String} html the content of the link.
1578 * @cfg {String} anchor name for the anchor link
1579 * @cfg {String} fa - favicon
1581 * @cfg {Boolean} preventDefault (true | false) default false
1585 * Create a new Input
1586 * @param {Object} config The config object
1589 Roo.bootstrap.Link = function(config){
1590 Roo.bootstrap.Link.superclass.constructor.call(this, config);
1596 * The img click event for the img.
1597 * @param {Roo.EventObject} e
1603 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
1607 preventDefault: false,
1613 getAutoCreate : function()
1615 var html = this.html || '';
1617 if (this.fa !== false) {
1618 html = '<i class="fa fa-' + this.fa + '"></i>';
1623 // anchor's do not require html/href...
1624 if (this.anchor === false) {
1626 cfg.href = this.href || '#';
1628 cfg.name = this.anchor;
1629 if (this.html !== false || this.fa !== false) {
1632 if (this.href !== false) {
1633 cfg.href = this.href;
1637 if(this.alt !== false){
1642 if(this.target !== false) {
1643 cfg.target = this.target;
1649 initEvents: function() {
1651 if(!this.href || this.preventDefault){
1652 this.el.on('click', this.onClick, this);
1656 onClick : function(e)
1658 if(this.preventDefault){
1661 //Roo.log('img onclick');
1662 this.fireEvent('click', this, e);
1675 * @class Roo.bootstrap.Header
1676 * @extends Roo.bootstrap.Component
1677 * Bootstrap Header class
1678 * @cfg {String} html content of header
1679 * @cfg {Number} level (1|2|3|4|5|6) default 1
1682 * Create a new Header
1683 * @param {Object} config The config object
1687 Roo.bootstrap.Header = function(config){
1688 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1691 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1699 getAutoCreate : function(){
1704 tag: 'h' + (1 *this.level),
1705 html: this.html || ''
1717 * Ext JS Library 1.1.1
1718 * Copyright(c) 2006-2007, Ext JS, LLC.
1720 * Originally Released Under LGPL - original licence link has changed is not relivant.
1723 * <script type="text/javascript">
1727 * @class Roo.bootstrap.MenuMgr
1728 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1731 Roo.bootstrap.MenuMgr = function(){
1732 var menus, active, groups = {}, attached = false, lastShow = new Date();
1734 // private - called when first menu is created
1737 active = new Roo.util.MixedCollection();
1738 Roo.get(document).addKeyListener(27, function(){
1739 if(active.length > 0){
1747 if(active && active.length > 0){
1748 var c = active.clone();
1758 if(active.length < 1){
1759 Roo.get(document).un("mouseup", onMouseDown);
1767 var last = active.last();
1768 lastShow = new Date();
1771 Roo.get(document).on("mouseup", onMouseDown);
1776 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1777 m.parentMenu.activeChild = m;
1778 }else if(last && last.isVisible()){
1779 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1784 function onBeforeHide(m){
1786 m.activeChild.hide();
1788 if(m.autoHideTimer){
1789 clearTimeout(m.autoHideTimer);
1790 delete m.autoHideTimer;
1795 function onBeforeShow(m){
1796 var pm = m.parentMenu;
1797 if(!pm && !m.allowOtherMenus){
1799 }else if(pm && pm.activeChild && active != m){
1800 pm.activeChild.hide();
1804 // private this should really trigger on mouseup..
1805 function onMouseDown(e){
1806 Roo.log("on Mouse Up");
1808 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1809 Roo.log("MenuManager hideAll");
1818 function onBeforeCheck(mi, state){
1820 var g = groups[mi.group];
1821 for(var i = 0, l = g.length; i < l; i++){
1823 g[i].setChecked(false);
1832 * Hides all menus that are currently visible
1834 hideAll : function(){
1839 register : function(menu){
1843 menus[menu.id] = menu;
1844 menu.on("beforehide", onBeforeHide);
1845 menu.on("hide", onHide);
1846 menu.on("beforeshow", onBeforeShow);
1847 menu.on("show", onShow);
1849 if(g && menu.events["checkchange"]){
1853 groups[g].push(menu);
1854 menu.on("checkchange", onCheck);
1859 * Returns a {@link Roo.menu.Menu} object
1860 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1861 * be used to generate and return a new Menu instance.
1863 get : function(menu){
1864 if(typeof menu == "string"){ // menu id
1866 }else if(menu.events){ // menu instance
1869 /*else if(typeof menu.length == 'number'){ // array of menu items?
1870 return new Roo.bootstrap.Menu({items:menu});
1871 }else{ // otherwise, must be a config
1872 return new Roo.bootstrap.Menu(menu);
1879 unregister : function(menu){
1880 delete menus[menu.id];
1881 menu.un("beforehide", onBeforeHide);
1882 menu.un("hide", onHide);
1883 menu.un("beforeshow", onBeforeShow);
1884 menu.un("show", onShow);
1886 if(g && menu.events["checkchange"]){
1887 groups[g].remove(menu);
1888 menu.un("checkchange", onCheck);
1893 registerCheckable : function(menuItem){
1894 var g = menuItem.group;
1899 groups[g].push(menuItem);
1900 menuItem.on("beforecheckchange", onBeforeCheck);
1905 unregisterCheckable : function(menuItem){
1906 var g = menuItem.group;
1908 groups[g].remove(menuItem);
1909 menuItem.un("beforecheckchange", onBeforeCheck);
1921 * @class Roo.bootstrap.Menu
1922 * @extends Roo.bootstrap.Component
1923 * Bootstrap Menu class - container for MenuItems
1924 * @cfg {String} type (dropdown|treeview|submenu) type of menu
1925 * @cfg {bool} hidden if the menu should be hidden when rendered.
1926 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
1927 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
1931 * @param {Object} config The config object
1935 Roo.bootstrap.Menu = function(config){
1936 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1937 if (this.registerMenu && this.type != 'treeview') {
1938 Roo.bootstrap.MenuMgr.register(this);
1943 * Fires before this menu is displayed
1944 * @param {Roo.menu.Menu} this
1949 * Fires before this menu is hidden
1950 * @param {Roo.menu.Menu} this
1955 * Fires after this menu is displayed
1956 * @param {Roo.menu.Menu} this
1961 * Fires after this menu is hidden
1962 * @param {Roo.menu.Menu} this
1967 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1968 * @param {Roo.menu.Menu} this
1969 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1970 * @param {Roo.EventObject} e
1975 * Fires when the mouse is hovering over this menu
1976 * @param {Roo.menu.Menu} this
1977 * @param {Roo.EventObject} e
1978 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1983 * Fires when the mouse exits this menu
1984 * @param {Roo.menu.Menu} this
1985 * @param {Roo.EventObject} e
1986 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1991 * Fires when a menu item contained in this menu is clicked
1992 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1993 * @param {Roo.EventObject} e
1997 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2000 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
2004 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
2007 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2009 registerMenu : true,
2011 menuItems :false, // stores the menu items..
2021 getChildContainer : function() {
2025 getAutoCreate : function(){
2027 //if (['right'].indexOf(this.align)!==-1) {
2028 // cfg.cn[1].cls += ' pull-right'
2034 cls : 'dropdown-menu' ,
2035 style : 'z-index:1000'
2039 if (this.type === 'submenu') {
2040 cfg.cls = 'submenu active';
2042 if (this.type === 'treeview') {
2043 cfg.cls = 'treeview-menu';
2048 initEvents : function() {
2050 // Roo.log("ADD event");
2051 // Roo.log(this.triggerEl.dom);
2053 this.triggerEl.on('click', this.onTriggerClick, this);
2055 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2057 this.triggerEl.addClass('dropdown-toggle');
2060 this.el.on('touchstart' , this.onTouch, this);
2062 this.el.on('click' , this.onClick, this);
2064 this.el.on("mouseover", this.onMouseOver, this);
2065 this.el.on("mouseout", this.onMouseOut, this);
2069 findTargetItem : function(e)
2071 var t = e.getTarget(".dropdown-menu-item", this.el, true);
2075 //Roo.log(t); Roo.log(t.id);
2077 //Roo.log(this.menuitems);
2078 return this.menuitems.get(t.id);
2080 //return this.items.get(t.menuItemId);
2086 onTouch : function(e)
2088 Roo.log("menu.onTouch");
2089 //e.stopEvent(); this make the user popdown broken
2093 onClick : function(e)
2095 Roo.log("menu.onClick");
2097 var t = this.findTargetItem(e);
2098 if(!t || t.isContainer){
2103 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
2104 if(t == this.activeItem && t.shouldDeactivate(e)){
2105 this.activeItem.deactivate();
2106 delete this.activeItem;
2110 this.setActiveItem(t, true);
2118 Roo.log('pass click event');
2122 this.fireEvent("click", this, t, e);
2126 if(!t.href.length || t.href == '#'){
2127 (function() { _this.hide(); }).defer(100);
2132 onMouseOver : function(e){
2133 var t = this.findTargetItem(e);
2136 // if(t.canActivate && !t.disabled){
2137 // this.setActiveItem(t, true);
2141 this.fireEvent("mouseover", this, e, t);
2143 isVisible : function(){
2144 return !this.hidden;
2146 onMouseOut : function(e){
2147 var t = this.findTargetItem(e);
2150 // if(t == this.activeItem && t.shouldDeactivate(e)){
2151 // this.activeItem.deactivate();
2152 // delete this.activeItem;
2155 this.fireEvent("mouseout", this, e, t);
2160 * Displays this menu relative to another element
2161 * @param {String/HTMLElement/Roo.Element} element The element to align to
2162 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2163 * the element (defaults to this.defaultAlign)
2164 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2166 show : function(el, pos, parentMenu){
2167 this.parentMenu = parentMenu;
2171 this.fireEvent("beforeshow", this);
2172 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2175 * Displays this menu at a specific xy position
2176 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2177 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2179 showAt : function(xy, parentMenu, /* private: */_e){
2180 this.parentMenu = parentMenu;
2185 this.fireEvent("beforeshow", this);
2186 //xy = this.el.adjustForConstraints(xy);
2190 this.hideMenuItems();
2191 this.hidden = false;
2192 this.triggerEl.addClass('open');
2194 if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2195 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2198 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2203 this.fireEvent("show", this);
2209 this.doFocus.defer(50, this);
2213 doFocus : function(){
2215 this.focusEl.focus();
2220 * Hides this menu and optionally all parent menus
2221 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2223 hide : function(deep)
2226 this.hideMenuItems();
2227 if(this.el && this.isVisible()){
2228 this.fireEvent("beforehide", this);
2229 if(this.activeItem){
2230 this.activeItem.deactivate();
2231 this.activeItem = null;
2233 this.triggerEl.removeClass('open');;
2235 this.fireEvent("hide", this);
2237 if(deep === true && this.parentMenu){
2238 this.parentMenu.hide(true);
2242 onTriggerClick : function(e)
2244 Roo.log('trigger click');
2246 var target = e.getTarget();
2248 Roo.log(target.nodeName.toLowerCase());
2250 if(target.nodeName.toLowerCase() === 'i'){
2256 onTriggerPress : function(e)
2258 Roo.log('trigger press');
2259 //Roo.log(e.getTarget());
2260 // Roo.log(this.triggerEl.dom);
2262 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2263 var pel = Roo.get(e.getTarget());
2264 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2265 Roo.log('is treeview or dropdown?');
2269 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2273 if (this.isVisible()) {
2278 this.show(this.triggerEl, false, false);
2281 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2288 hideMenuItems : function()
2290 Roo.log("hide Menu Items");
2294 //$(backdrop).remove()
2295 this.el.select('.open',true).each(function(aa) {
2297 aa.removeClass('open');
2298 //var parent = getParent($(this))
2299 //var relatedTarget = { relatedTarget: this }
2301 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2302 //if (e.isDefaultPrevented()) return
2303 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2306 addxtypeChild : function (tree, cntr) {
2307 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2309 this.menuitems.add(comp);
2330 * @class Roo.bootstrap.MenuItem
2331 * @extends Roo.bootstrap.Component
2332 * Bootstrap MenuItem class
2333 * @cfg {String} html the menu label
2334 * @cfg {String} href the link
2335 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2336 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2337 * @cfg {Boolean} active used on sidebars to highlight active itesm
2338 * @cfg {String} fa favicon to show on left of menu item.
2339 * @cfg {Roo.bootsrap.Menu} menu the child menu.
2343 * Create a new MenuItem
2344 * @param {Object} config The config object
2348 Roo.bootstrap.MenuItem = function(config){
2349 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2354 * The raw click event for the entire grid.
2355 * @param {Roo.bootstrap.MenuItem} this
2356 * @param {Roo.EventObject} e
2362 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2366 preventDefault: false,
2367 isContainer : false,
2371 getAutoCreate : function(){
2373 if(this.isContainer){
2376 cls: 'dropdown-menu-item'
2390 if (this.fa !== false) {
2393 cls : 'fa fa-' + this.fa
2402 cls: 'dropdown-menu-item',
2405 if (this.parent().type == 'treeview') {
2406 cfg.cls = 'treeview-menu';
2409 cfg.cls += ' active';
2414 anc.href = this.href || cfg.cn[0].href ;
2415 ctag.html = this.html || cfg.cn[0].html ;
2419 initEvents: function()
2421 if (this.parent().type == 'treeview') {
2422 this.el.select('a').on('click', this.onClick, this);
2426 this.menu.parentType = this.xtype;
2427 this.menu.triggerEl = this.el;
2428 this.menu = this.addxtype(Roo.apply({}, this.menu));
2432 onClick : function(e)
2434 Roo.log('item on click ');
2436 if(this.preventDefault){
2439 //this.parent().hideMenuItems();
2441 this.fireEvent('click', this, e);
2460 * @class Roo.bootstrap.MenuSeparator
2461 * @extends Roo.bootstrap.Component
2462 * Bootstrap MenuSeparator class
2465 * Create a new MenuItem
2466 * @param {Object} config The config object
2470 Roo.bootstrap.MenuSeparator = function(config){
2471 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2474 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2476 getAutoCreate : function(){
2495 * @class Roo.bootstrap.Modal
2496 * @extends Roo.bootstrap.Component
2497 * Bootstrap Modal class
2498 * @cfg {String} title Title of dialog
2499 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2500 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2501 * @cfg {Boolean} specificTitle default false
2502 * @cfg {Array} buttons Array of buttons or standard button set..
2503 * @cfg {String} buttonPosition (left|right|center) default right
2504 * @cfg {Boolean} animate default true
2505 * @cfg {Boolean} allow_close default true
2506 * @cfg {Boolean} fitwindow default false
2507 * @cfg {String} size (sm|lg) default empty
2511 * Create a new Modal Dialog
2512 * @param {Object} config The config object
2515 Roo.bootstrap.Modal = function(config){
2516 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2521 * The raw btnclick event for the button
2522 * @param {Roo.EventObject} e
2527 * Fire when dialog resize
2528 * @param {Roo.bootstrap.Modal} this
2529 * @param {Roo.EventObject} e
2533 this.buttons = this.buttons || [];
2536 this.tmpl = Roo.factory(this.tmpl);
2541 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2543 title : 'test dialog',
2553 specificTitle: false,
2555 buttonPosition: 'right',
2574 onRender : function(ct, position)
2576 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2579 var cfg = Roo.apply({}, this.getAutoCreate());
2582 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2584 //if (!cfg.name.length) {
2588 cfg.cls += ' ' + this.cls;
2591 cfg.style = this.style;
2593 this.el = Roo.get(document.body).createChild(cfg, position);
2595 //var type = this.el.dom.type;
2598 if(this.tabIndex !== undefined){
2599 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2602 this.dialogEl = this.el.select('.modal-dialog',true).first();
2603 this.bodyEl = this.el.select('.modal-body',true).first();
2604 this.closeEl = this.el.select('.modal-header .close', true).first();
2605 this.headerEl = this.el.select('.modal-header',true).first();
2606 this.titleEl = this.el.select('.modal-title',true).first();
2607 this.footerEl = this.el.select('.modal-footer',true).first();
2609 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2610 this.maskEl.enableDisplayMode("block");
2612 //this.el.addClass("x-dlg-modal");
2614 if (this.buttons.length) {
2615 Roo.each(this.buttons, function(bb) {
2616 var b = Roo.apply({}, bb);
2617 b.xns = b.xns || Roo.bootstrap;
2618 b.xtype = b.xtype || 'Button';
2619 if (typeof(b.listeners) == 'undefined') {
2620 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2623 var btn = Roo.factory(b);
2625 btn.render(this.el.select('.modal-footer div').first());
2629 // render the children.
2632 if(typeof(this.items) != 'undefined'){
2633 var items = this.items;
2636 for(var i =0;i < items.length;i++) {
2637 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2641 this.items = nitems;
2643 // where are these used - they used to be body/close/footer
2647 //this.el.addClass([this.fieldClass, this.cls]);
2651 getAutoCreate : function(){
2656 html : this.html || ''
2661 cls : 'modal-title',
2665 if(this.specificTitle){
2671 if (this.allow_close) {
2683 if(this.size.length){
2684 size = 'modal-' + this.size;
2689 style : 'display: none',
2692 cls: "modal-dialog " + size,
2695 cls : "modal-content",
2698 cls : 'modal-header',
2703 cls : 'modal-footer',
2707 cls: 'btn-' + this.buttonPosition
2724 modal.cls += ' fade';
2730 getChildContainer : function() {
2735 getButtonContainer : function() {
2736 return this.el.select('.modal-footer div',true).first();
2739 initEvents : function()
2741 if (this.allow_close) {
2742 this.closeEl.on('click', this.hide, this);
2744 Roo.EventManager.onWindowResize(this.resize, this, true);
2751 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2752 if (this.fitwindow) {
2753 var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2754 var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2759 setSize : function(w,h)
2769 if (!this.rendered) {
2773 this.el.setStyle('display', 'block');
2775 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
2778 this.el.addClass('in');
2781 this.el.addClass('in');
2785 // not sure how we can show data in here..
2787 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2790 Roo.get(document.body).addClass("x-body-masked");
2792 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2793 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2798 this.fireEvent('show', this);
2800 // set zindex here - otherwise it appears to be ignored...
2801 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2804 this.items.forEach( function(e) {
2805 e.layout ? e.layout() : false;
2813 if(this.fireEvent("beforehide", this) !== false){
2815 Roo.get(document.body).removeClass("x-body-masked");
2816 this.el.removeClass('in');
2817 this.el.select('.modal-dialog', true).first().setStyle('transform','');
2819 if(this.animate){ // why
2821 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2823 this.el.setStyle('display', 'none');
2825 this.fireEvent('hide', this);
2829 addButton : function(str, cb)
2833 var b = Roo.apply({}, { html : str } );
2834 b.xns = b.xns || Roo.bootstrap;
2835 b.xtype = b.xtype || 'Button';
2836 if (typeof(b.listeners) == 'undefined') {
2837 b.listeners = { click : cb.createDelegate(this) };
2840 var btn = Roo.factory(b);
2842 btn.render(this.el.select('.modal-footer div').first());
2848 setDefaultButton : function(btn)
2850 //this.el.select('.modal-footer').()
2854 resizeTo: function(w,h)
2858 this.dialogEl.setWidth(w);
2859 if (this.diff === false) {
2860 this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2863 this.bodyEl.setHeight(h-this.diff);
2865 this.fireEvent('resize', this);
2868 setContentSize : function(w, h)
2872 onButtonClick: function(btn,e)
2875 this.fireEvent('btnclick', btn.name, e);
2878 * Set the title of the Dialog
2879 * @param {String} str new Title
2881 setTitle: function(str) {
2882 this.titleEl.dom.innerHTML = str;
2885 * Set the body of the Dialog
2886 * @param {String} str new Title
2888 setBody: function(str) {
2889 this.bodyEl.dom.innerHTML = str;
2892 * Set the body of the Dialog using the template
2893 * @param {Obj} data - apply this data to the template and replace the body contents.
2895 applyBody: function(obj)
2898 Roo.log("Error - using apply Body without a template");
2901 this.tmpl.overwrite(this.bodyEl, obj);
2907 Roo.apply(Roo.bootstrap.Modal, {
2909 * Button config that displays a single OK button
2918 * Button config that displays Yes and No buttons
2934 * Button config that displays OK and Cancel buttons
2949 * Button config that displays Yes, No and Cancel buttons
2973 * messagebox - can be used as a replace
2977 * @class Roo.MessageBox
2978 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
2982 Roo.Msg.alert('Status', 'Changes saved successfully.');
2984 // Prompt for user data:
2985 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2987 // process text value...
2991 // Show a dialog using config options:
2993 title:'Save Changes?',
2994 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2995 buttons: Roo.Msg.YESNOCANCEL,
3002 Roo.bootstrap.MessageBox = function(){
3003 var dlg, opt, mask, waitTimer;
3004 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3005 var buttons, activeTextEl, bwidth;
3009 var handleButton = function(button){
3011 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3015 var handleHide = function(){
3017 dlg.el.removeClass(opt.cls);
3020 // Roo.TaskMgr.stop(waitTimer);
3021 // waitTimer = null;
3026 var updateButtons = function(b){
3029 buttons["ok"].hide();
3030 buttons["cancel"].hide();
3031 buttons["yes"].hide();
3032 buttons["no"].hide();
3033 //dlg.footer.dom.style.display = 'none';
3036 dlg.footerEl.dom.style.display = '';
3037 for(var k in buttons){
3038 if(typeof buttons[k] != "function"){
3041 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3042 width += buttons[k].el.getWidth()+15;
3052 var handleEsc = function(d, k, e){
3053 if(opt && opt.closable !== false){
3063 * Returns a reference to the underlying {@link Roo.BasicDialog} element
3064 * @return {Roo.BasicDialog} The BasicDialog element
3066 getDialog : function(){
3068 dlg = new Roo.bootstrap.Modal( {
3071 //constraintoviewport:false,
3073 //collapsible : false,
3078 //buttonAlign:"center",
3079 closeClick : function(){
3080 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3083 handleButton("cancel");
3088 dlg.on("hide", handleHide);
3090 //dlg.addKeyListener(27, handleEsc);
3092 this.buttons = buttons;
3093 var bt = this.buttonText;
3094 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3095 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3096 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3097 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3099 bodyEl = dlg.bodyEl.createChild({
3101 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3102 '<textarea class="roo-mb-textarea"></textarea>' +
3103 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
3105 msgEl = bodyEl.dom.firstChild;
3106 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3107 textboxEl.enableDisplayMode();
3108 textboxEl.addKeyListener([10,13], function(){
3109 if(dlg.isVisible() && opt && opt.buttons){
3112 }else if(opt.buttons.yes){
3113 handleButton("yes");
3117 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3118 textareaEl.enableDisplayMode();
3119 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3120 progressEl.enableDisplayMode();
3122 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3123 //var pf = progressEl.dom.firstChild;
3125 //pp = Roo.get(pf.firstChild);
3126 //pp.setHeight(pf.offsetHeight);
3134 * Updates the message box body text
3135 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3136 * the XHTML-compliant non-breaking space character '&#160;')
3137 * @return {Roo.MessageBox} This message box
3139 updateText : function(text)
3141 if(!dlg.isVisible() && !opt.width){
3142 dlg.dialogEl.setWidth(this.maxWidth);
3143 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3145 msgEl.innerHTML = text || ' ';
3147 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3148 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3150 Math.min(opt.width || cw , this.maxWidth),
3151 Math.max(opt.minWidth || this.minWidth, bwidth)
3154 activeTextEl.setWidth(w);
3156 if(dlg.isVisible()){
3157 dlg.fixedcenter = false;
3159 // to big, make it scroll. = But as usual stupid IE does not support
3162 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3163 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3164 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3166 bodyEl.dom.style.height = '';
3167 bodyEl.dom.style.overflowY = '';
3170 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3172 bodyEl.dom.style.overflowX = '';
3175 dlg.setContentSize(w, bodyEl.getHeight());
3176 if(dlg.isVisible()){
3177 dlg.fixedcenter = true;
3183 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
3184 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3185 * @param {Number} value Any number between 0 and 1 (e.g., .5)
3186 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3187 * @return {Roo.MessageBox} This message box
3189 updateProgress : function(value, text){
3191 this.updateText(text);
3193 if (pp) { // weird bug on my firefox - for some reason this is not defined
3194 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3200 * Returns true if the message box is currently displayed
3201 * @return {Boolean} True if the message box is visible, else false
3203 isVisible : function(){
3204 return dlg && dlg.isVisible();
3208 * Hides the message box if it is displayed
3211 if(this.isVisible()){
3217 * Displays a new message box, or reinitializes an existing message box, based on the config options
3218 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3219 * The following config object properties are supported:
3221 Property Type Description
3222 ---------- --------------- ------------------------------------------------------------------------------------
3223 animEl String/Element An id or Element from which the message box should animate as it opens and
3224 closes (defaults to undefined)
3225 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3226 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3227 closable Boolean False to hide the top-right close button (defaults to true). Note that
3228 progress and wait dialogs will ignore this property and always hide the
3229 close button as they can only be closed programmatically.
3230 cls String A custom CSS class to apply to the message box element
3231 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3232 displayed (defaults to 75)
3233 fn Function A callback function to execute after closing the dialog. The arguments to the
3234 function will be btn (the name of the button that was clicked, if applicable,
3235 e.g. "ok"), and text (the value of the active text field, if applicable).
3236 Progress and wait dialogs will ignore this option since they do not respond to
3237 user actions and can only be closed programmatically, so any required function
3238 should be called by the same code after it closes the dialog.
3239 icon String A CSS class that provides a background image to be used as an icon for
3240 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3241 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3242 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3243 modal Boolean False to allow user interaction with the page while the message box is
3244 displayed (defaults to true)
3245 msg String A string that will replace the existing message box body text (defaults
3246 to the XHTML-compliant non-breaking space character ' ')
3247 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3248 progress Boolean True to display a progress bar (defaults to false)
3249 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3250 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3251 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3252 title String The title text
3253 value String The string value to set into the active textbox element if displayed
3254 wait Boolean True to display a progress bar (defaults to false)
3255 width Number The width of the dialog in pixels
3262 msg: 'Please enter your address:',
3264 buttons: Roo.MessageBox.OKCANCEL,
3267 animEl: 'addAddressBtn'
3270 * @param {Object} config Configuration options
3271 * @return {Roo.MessageBox} This message box
3273 show : function(options)
3276 // this causes nightmares if you show one dialog after another
3277 // especially on callbacks..
3279 if(this.isVisible()){
3282 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3283 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3284 Roo.log("New Dialog Message:" + options.msg )
3285 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3286 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3289 var d = this.getDialog();
3291 d.setTitle(opt.title || " ");
3292 d.closeEl.setDisplayed(opt.closable !== false);
3293 activeTextEl = textboxEl;
3294 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3299 textareaEl.setHeight(typeof opt.multiline == "number" ?
3300 opt.multiline : this.defaultTextHeight);
3301 activeTextEl = textareaEl;
3310 progressEl.setDisplayed(opt.progress === true);
3311 this.updateProgress(0);
3312 activeTextEl.dom.value = opt.value || "";
3314 dlg.setDefaultButton(activeTextEl);
3316 var bs = opt.buttons;
3320 }else if(bs && bs.yes){
3321 db = buttons["yes"];
3323 dlg.setDefaultButton(db);
3325 bwidth = updateButtons(opt.buttons);
3326 this.updateText(opt.msg);
3328 d.el.addClass(opt.cls);
3330 d.proxyDrag = opt.proxyDrag === true;
3331 d.modal = opt.modal !== false;
3332 d.mask = opt.modal !== false ? mask : false;
3334 // force it to the end of the z-index stack so it gets a cursor in FF
3335 document.body.appendChild(dlg.el.dom);
3336 d.animateTarget = null;
3337 d.show(options.animEl);
3343 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3344 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3345 * and closing the message box when the process is complete.
3346 * @param {String} title The title bar text
3347 * @param {String} msg The message box body text
3348 * @return {Roo.MessageBox} This message box
3350 progress : function(title, msg){
3357 minWidth: this.minProgressWidth,
3364 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3365 * If a callback function is passed it will be called after the user clicks the button, and the
3366 * id of the button that was clicked will be passed as the only parameter to the callback
3367 * (could also be the top-right close button).
3368 * @param {String} title The title bar text
3369 * @param {String} msg The message box body text
3370 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3371 * @param {Object} scope (optional) The scope of the callback function
3372 * @return {Roo.MessageBox} This message box
3374 alert : function(title, msg, fn, scope)
3389 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3390 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3391 * You are responsible for closing the message box when the process is complete.
3392 * @param {String} msg The message box body text
3393 * @param {String} title (optional) The title bar text
3394 * @return {Roo.MessageBox} This message box
3396 wait : function(msg, title){
3407 waitTimer = Roo.TaskMgr.start({
3409 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3417 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3418 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3419 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3420 * @param {String} title The title bar text
3421 * @param {String} msg The message box body text
3422 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3423 * @param {Object} scope (optional) The scope of the callback function
3424 * @return {Roo.MessageBox} This message box
3426 confirm : function(title, msg, fn, scope){
3430 buttons: this.YESNO,
3439 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3440 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3441 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3442 * (could also be the top-right close button) and the text that was entered will be passed as the two
3443 * parameters to the callback.
3444 * @param {String} title The title bar text
3445 * @param {String} msg The message box body text
3446 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3447 * @param {Object} scope (optional) The scope of the callback function
3448 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3449 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3450 * @return {Roo.MessageBox} This message box
3452 prompt : function(title, msg, fn, scope, multiline){
3456 buttons: this.OKCANCEL,
3461 multiline: multiline,
3468 * Button config that displays a single OK button
3473 * Button config that displays Yes and No buttons
3476 YESNO : {yes:true, no:true},
3478 * Button config that displays OK and Cancel buttons
3481 OKCANCEL : {ok:true, cancel:true},
3483 * Button config that displays Yes, No and Cancel buttons
3486 YESNOCANCEL : {yes:true, no:true, cancel:true},
3489 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3492 defaultTextHeight : 75,
3494 * The maximum width in pixels of the message box (defaults to 600)
3499 * The minimum width in pixels of the message box (defaults to 100)
3504 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3505 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3508 minProgressWidth : 250,
3510 * An object containing the default button text strings that can be overriden for localized language support.
3511 * Supported properties are: ok, cancel, yes and no.
3512 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3525 * Shorthand for {@link Roo.MessageBox}
3527 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3528 Roo.Msg = Roo.Msg || Roo.MessageBox;
3537 * @class Roo.bootstrap.Navbar
3538 * @extends Roo.bootstrap.Component
3539 * Bootstrap Navbar class
3542 * Create a new Navbar
3543 * @param {Object} config The config object
3547 Roo.bootstrap.Navbar = function(config){
3548 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3552 * @event beforetoggle
3553 * Fire before toggle the menu
3554 * @param {Roo.EventObject} e
3556 "beforetoggle" : true
3560 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3569 getAutoCreate : function(){
3572 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3576 initEvents :function ()
3578 //Roo.log(this.el.select('.navbar-toggle',true));
3579 this.el.select('.navbar-toggle',true).on('click', function() {
3580 if(this.fireEvent('beforetoggle', this) !== false){
3581 this.el.select('.navbar-collapse',true).toggleClass('in');
3591 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3593 var size = this.el.getSize();
3594 this.maskEl.setSize(size.width, size.height);
3595 this.maskEl.enableDisplayMode("block");
3604 getChildContainer : function()
3606 if (this.el.select('.collapse').getCount()) {
3607 return this.el.select('.collapse',true).first();
3640 * @class Roo.bootstrap.NavSimplebar
3641 * @extends Roo.bootstrap.Navbar
3642 * Bootstrap Sidebar class
3644 * @cfg {Boolean} inverse is inverted color
3646 * @cfg {String} type (nav | pills | tabs)
3647 * @cfg {Boolean} arrangement stacked | justified
3648 * @cfg {String} align (left | right) alignment
3650 * @cfg {Boolean} main (true|false) main nav bar? default false
3651 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3653 * @cfg {String} tag (header|footer|nav|div) default is nav
3659 * Create a new Sidebar
3660 * @param {Object} config The config object
3664 Roo.bootstrap.NavSimplebar = function(config){
3665 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3668 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3684 getAutoCreate : function(){
3688 tag : this.tag || 'div',
3701 this.type = this.type || 'nav';
3702 if (['tabs','pills'].indexOf(this.type)!==-1) {
3703 cfg.cn[0].cls += ' nav-' + this.type
3707 if (this.type!=='nav') {
3708 Roo.log('nav type must be nav/tabs/pills')
3710 cfg.cn[0].cls += ' navbar-nav'
3716 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3717 cfg.cn[0].cls += ' nav-' + this.arrangement;
3721 if (this.align === 'right') {
3722 cfg.cn[0].cls += ' navbar-right';
3726 cfg.cls += ' navbar-inverse';
3753 * @class Roo.bootstrap.NavHeaderbar
3754 * @extends Roo.bootstrap.NavSimplebar
3755 * Bootstrap Sidebar class
3757 * @cfg {String} brand what is brand
3758 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3759 * @cfg {String} brand_href href of the brand
3760 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
3761 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3762 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3763 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3766 * Create a new Sidebar
3767 * @param {Object} config The config object
3771 Roo.bootstrap.NavHeaderbar = function(config){
3772 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3776 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3783 desktopCenter : false,
3786 getAutoCreate : function(){
3789 tag: this.nav || 'nav',
3796 if (this.desktopCenter) {
3797 cn.push({cls : 'container', cn : []});
3804 cls: 'navbar-header',
3809 cls: 'navbar-toggle',
3810 'data-toggle': 'collapse',
3815 html: 'Toggle navigation'
3837 cls: 'collapse navbar-collapse',
3841 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3843 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3844 cfg.cls += ' navbar-' + this.position;
3846 // tag can override this..
3848 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
3851 if (this.brand !== '') {
3854 href: this.brand_href ? this.brand_href : '#',
3855 cls: 'navbar-brand',
3863 cfg.cls += ' main-nav';
3871 getHeaderChildContainer : function()
3873 if (this.srButton && this.el.select('.navbar-header').getCount()) {
3874 return this.el.select('.navbar-header',true).first();
3877 return this.getChildContainer();
3881 initEvents : function()
3883 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3885 if (this.autohide) {
3890 Roo.get(document).on('scroll',function(e) {
3891 var ns = Roo.get(document).getScroll().top;
3892 var os = prevScroll;
3896 ft.removeClass('slideDown');
3897 ft.addClass('slideUp');
3900 ft.removeClass('slideUp');
3901 ft.addClass('slideDown');
3922 * @class Roo.bootstrap.NavSidebar
3923 * @extends Roo.bootstrap.Navbar
3924 * Bootstrap Sidebar class
3927 * Create a new Sidebar
3928 * @param {Object} config The config object
3932 Roo.bootstrap.NavSidebar = function(config){
3933 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3936 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
3938 sidebar : true, // used by Navbar Item and NavbarGroup at present...
3940 getAutoCreate : function(){
3945 cls: 'sidebar sidebar-nav'
3967 * @class Roo.bootstrap.NavGroup
3968 * @extends Roo.bootstrap.Component
3969 * Bootstrap NavGroup class
3970 * @cfg {String} align (left|right)
3971 * @cfg {Boolean} inverse
3972 * @cfg {String} type (nav|pills|tab) default nav
3973 * @cfg {String} navId - reference Id for navbar.
3977 * Create a new nav group
3978 * @param {Object} config The config object
3981 Roo.bootstrap.NavGroup = function(config){
3982 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3985 Roo.bootstrap.NavGroup.register(this);
3989 * Fires when the active item changes
3990 * @param {Roo.bootstrap.NavGroup} this
3991 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3992 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
3999 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
4010 getAutoCreate : function()
4012 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4019 if (['tabs','pills'].indexOf(this.type)!==-1) {
4020 cfg.cls += ' nav-' + this.type
4022 if (this.type!=='nav') {
4023 Roo.log('nav type must be nav/tabs/pills')
4025 cfg.cls += ' navbar-nav'
4028 if (this.parent().sidebar) {
4031 cls: 'dashboard-menu sidebar-menu'
4037 if (this.form === true) {
4043 if (this.align === 'right') {
4044 cfg.cls += ' navbar-right';
4046 cfg.cls += ' navbar-left';
4050 if (this.align === 'right') {
4051 cfg.cls += ' navbar-right';
4055 cfg.cls += ' navbar-inverse';
4063 * sets the active Navigation item
4064 * @param {Roo.bootstrap.NavItem} the new current navitem
4066 setActiveItem : function(item)
4069 Roo.each(this.navItems, function(v){
4074 v.setActive(false, true);
4081 item.setActive(true, true);
4082 this.fireEvent('changed', this, item, prev);
4087 * gets the active Navigation item
4088 * @return {Roo.bootstrap.NavItem} the current navitem
4090 getActive : function()
4094 Roo.each(this.navItems, function(v){
4105 indexOfNav : function()
4109 Roo.each(this.navItems, function(v,i){
4120 * adds a Navigation item
4121 * @param {Roo.bootstrap.NavItem} the navitem to add
4123 addItem : function(cfg)
4125 var cn = new Roo.bootstrap.NavItem(cfg);
4127 cn.parentId = this.id;
4128 cn.onRender(this.el, null);
4132 * register a Navigation item
4133 * @param {Roo.bootstrap.NavItem} the navitem to add
4135 register : function(item)
4137 this.navItems.push( item);
4138 item.navId = this.navId;
4143 * clear all the Navigation item
4146 clearAll : function()
4149 this.el.dom.innerHTML = '';
4152 getNavItem: function(tabId)
4155 Roo.each(this.navItems, function(e) {
4156 if (e.tabId == tabId) {
4166 setActiveNext : function()
4168 var i = this.indexOfNav(this.getActive());
4169 if (i > this.navItems.length) {
4172 this.setActiveItem(this.navItems[i+1]);
4174 setActivePrev : function()
4176 var i = this.indexOfNav(this.getActive());
4180 this.setActiveItem(this.navItems[i-1]);
4182 clearWasActive : function(except) {
4183 Roo.each(this.navItems, function(e) {
4184 if (e.tabId != except.tabId && e.was_active) {
4185 e.was_active = false;
4192 getWasActive : function ()
4195 Roo.each(this.navItems, function(e) {
4210 Roo.apply(Roo.bootstrap.NavGroup, {
4214 * register a Navigation Group
4215 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4217 register : function(navgrp)
4219 this.groups[navgrp.navId] = navgrp;
4223 * fetch a Navigation Group based on the navigation ID
4224 * @param {string} the navgroup to add
4225 * @returns {Roo.bootstrap.NavGroup} the navgroup
4227 get: function(navId) {
4228 if (typeof(this.groups[navId]) == 'undefined') {
4230 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4232 return this.groups[navId] ;
4247 * @class Roo.bootstrap.NavItem
4248 * @extends Roo.bootstrap.Component
4249 * Bootstrap Navbar.NavItem class
4250 * @cfg {String} href link to
4251 * @cfg {String} html content of button
4252 * @cfg {String} badge text inside badge
4253 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4254 * @cfg {String} glyphicon name of glyphicon
4255 * @cfg {String} icon name of font awesome icon
4256 * @cfg {Boolean} active Is item active
4257 * @cfg {Boolean} disabled Is item disabled
4259 * @cfg {Boolean} preventDefault (true | false) default false
4260 * @cfg {String} tabId the tab that this item activates.
4261 * @cfg {String} tagtype (a|span) render as a href or span?
4262 * @cfg {Boolean} animateRef (true|false) link to element default false
4265 * Create a new Navbar Item
4266 * @param {Object} config The config object
4268 Roo.bootstrap.NavItem = function(config){
4269 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4274 * The raw click event for the entire grid.
4275 * @param {Roo.EventObject} e
4280 * Fires when the active item active state changes
4281 * @param {Roo.bootstrap.NavItem} this
4282 * @param {boolean} state the new state
4288 * Fires when scroll to element
4289 * @param {Roo.bootstrap.NavItem} this
4290 * @param {Object} options
4291 * @param {Roo.EventObject} e
4299 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4307 preventDefault : false,
4314 getAutoCreate : function(){
4323 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4325 if (this.disabled) {
4326 cfg.cls += ' disabled';
4329 if (this.href || this.html || this.glyphicon || this.icon) {
4333 href : this.href || "#",
4334 html: this.html || ''
4339 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4342 if(this.glyphicon) {
4343 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4348 cfg.cn[0].html += " <span class='caret'></span>";
4352 if (this.badge !== '') {
4354 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4362 initEvents: function()
4364 if (typeof (this.menu) != 'undefined') {
4365 this.menu.parentType = this.xtype;
4366 this.menu.triggerEl = this.el;
4367 this.menu = this.addxtype(Roo.apply({}, this.menu));
4370 this.el.select('a',true).on('click', this.onClick, this);
4372 if(this.tagtype == 'span'){
4373 this.el.select('span',true).on('click', this.onClick, this);
4376 // at this point parent should be available..
4377 this.parent().register(this);
4380 onClick : function(e)
4382 if (e.getTarget('.dropdown-menu-item')) {
4383 // did you click on a menu itemm.... - then don't trigger onclick..
4388 this.preventDefault ||
4391 Roo.log("NavItem - prevent Default?");
4395 if (this.disabled) {
4399 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4400 if (tg && tg.transition) {
4401 Roo.log("waiting for the transitionend");
4407 //Roo.log("fire event clicked");
4408 if(this.fireEvent('click', this, e) === false){
4412 if(this.tagtype == 'span'){
4416 //Roo.log(this.href);
4417 var ael = this.el.select('a',true).first();
4420 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4421 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4422 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4423 return; // ignore... - it's a 'hash' to another page.
4425 Roo.log("NavItem - prevent Default?");
4427 this.scrollToElement(e);
4431 var p = this.parent();
4433 if (['tabs','pills'].indexOf(p.type)!==-1) {
4434 if (typeof(p.setActiveItem) !== 'undefined') {
4435 p.setActiveItem(this);
4439 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4440 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4441 // remove the collapsed menu expand...
4442 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4446 isActive: function () {
4449 setActive : function(state, fire, is_was_active)
4451 if (this.active && !state && this.navId) {
4452 this.was_active = true;
4453 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4455 nv.clearWasActive(this);
4459 this.active = state;
4462 this.el.removeClass('active');
4463 } else if (!this.el.hasClass('active')) {
4464 this.el.addClass('active');
4467 this.fireEvent('changed', this, state);
4470 // show a panel if it's registered and related..
4472 if (!this.navId || !this.tabId || !state || is_was_active) {
4476 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4480 var pan = tg.getPanelByName(this.tabId);
4484 // if we can not flip to new panel - go back to old nav highlight..
4485 if (false == tg.showPanel(pan)) {
4486 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4488 var onav = nv.getWasActive();
4490 onav.setActive(true, false, true);
4499 // this should not be here...
4500 setDisabled : function(state)
4502 this.disabled = state;
4504 this.el.removeClass('disabled');
4505 } else if (!this.el.hasClass('disabled')) {
4506 this.el.addClass('disabled');
4512 * Fetch the element to display the tooltip on.
4513 * @return {Roo.Element} defaults to this.el
4515 tooltipEl : function()
4517 return this.el.select('' + this.tagtype + '', true).first();
4520 scrollToElement : function(e)
4522 var c = document.body;
4525 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4527 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4528 c = document.documentElement;
4531 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4537 var o = target.calcOffsetsTo(c);
4544 this.fireEvent('scrollto', this, options, e);
4546 Roo.get(c).scrollTo('top', options.value, true);
4559 * <span> icon </span>
4560 * <span> text </span>
4561 * <span>badge </span>
4565 * @class Roo.bootstrap.NavSidebarItem
4566 * @extends Roo.bootstrap.NavItem
4567 * Bootstrap Navbar.NavSidebarItem class
4568 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4569 * {bool} open is the menu open
4571 * Create a new Navbar Button
4572 * @param {Object} config The config object
4574 Roo.bootstrap.NavSidebarItem = function(config){
4575 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4580 * The raw click event for the entire grid.
4581 * @param {Roo.EventObject} e
4586 * Fires when the active item active state changes
4587 * @param {Roo.bootstrap.NavSidebarItem} this
4588 * @param {boolean} state the new state
4596 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4598 badgeWeight : 'default',
4602 getAutoCreate : function(){
4607 href : this.href || '#',
4619 html : this.html || ''
4624 cfg.cls += ' active';
4627 if (this.disabled) {
4628 cfg.cls += ' disabled';
4631 cfg.cls += ' open x-open';
4634 if (this.glyphicon || this.icon) {
4635 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4636 a.cn.push({ tag : 'i', cls : c }) ;
4641 if (this.badge !== '') {
4643 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4647 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4648 a.cls += 'dropdown-toggle treeview' ;
4656 initEvents : function()
4658 if (typeof (this.menu) != 'undefined') {
4659 this.menu.parentType = this.xtype;
4660 this.menu.triggerEl = this.el;
4661 this.menu = this.addxtype(Roo.apply({}, this.menu));
4664 this.el.on('click', this.onClick, this);
4667 if(this.badge !== ''){
4669 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4674 onClick : function(e)
4681 if(this.preventDefault){
4685 this.fireEvent('click', this);
4688 disable : function()
4690 this.setDisabled(true);
4695 this.setDisabled(false);
4698 setDisabled : function(state)
4700 if(this.disabled == state){
4704 this.disabled = state;
4707 this.el.addClass('disabled');
4711 this.el.removeClass('disabled');
4716 setActive : function(state)
4718 if(this.active == state){
4722 this.active = state;
4725 this.el.addClass('active');
4729 this.el.removeClass('active');
4734 isActive: function ()
4739 setBadge : function(str)
4745 this.badgeEl.dom.innerHTML = str;
4762 * @class Roo.bootstrap.Row
4763 * @extends Roo.bootstrap.Component
4764 * Bootstrap Row class (contains columns...)
4768 * @param {Object} config The config object
4771 Roo.bootstrap.Row = function(config){
4772 Roo.bootstrap.Row.superclass.constructor.call(this, config);
4775 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
4777 getAutoCreate : function(){
4796 * @class Roo.bootstrap.Element
4797 * @extends Roo.bootstrap.Component
4798 * Bootstrap Element class
4799 * @cfg {String} html contents of the element
4800 * @cfg {String} tag tag of the element
4801 * @cfg {String} cls class of the element
4802 * @cfg {Boolean} preventDefault (true|false) default false
4803 * @cfg {Boolean} clickable (true|false) default false
4806 * Create a new Element
4807 * @param {Object} config The config object
4810 Roo.bootstrap.Element = function(config){
4811 Roo.bootstrap.Element.superclass.constructor.call(this, config);
4817 * When a element is chick
4818 * @param {Roo.bootstrap.Element} this
4819 * @param {Roo.EventObject} e
4825 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
4830 preventDefault: false,
4833 getAutoCreate : function(){
4844 initEvents: function()
4846 Roo.bootstrap.Element.superclass.initEvents.call(this);
4849 this.el.on('click', this.onClick, this);
4854 onClick : function(e)
4856 if(this.preventDefault){
4860 this.fireEvent('click', this, e);
4863 getValue : function()
4865 return this.el.dom.innerHTML;
4868 setValue : function(value)
4870 this.el.dom.innerHTML = value;
4885 * @class Roo.bootstrap.Pagination
4886 * @extends Roo.bootstrap.Component
4887 * Bootstrap Pagination class
4888 * @cfg {String} size xs | sm | md | lg
4889 * @cfg {Boolean} inverse false | true
4892 * Create a new Pagination
4893 * @param {Object} config The config object
4896 Roo.bootstrap.Pagination = function(config){
4897 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4900 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
4906 getAutoCreate : function(){
4912 cfg.cls += ' inverse';
4918 cfg.cls += " " + this.cls;
4936 * @class Roo.bootstrap.PaginationItem
4937 * @extends Roo.bootstrap.Component
4938 * Bootstrap PaginationItem class
4939 * @cfg {String} html text
4940 * @cfg {String} href the link
4941 * @cfg {Boolean} preventDefault (true | false) default true
4942 * @cfg {Boolean} active (true | false) default false
4943 * @cfg {Boolean} disabled default false
4947 * Create a new PaginationItem
4948 * @param {Object} config The config object
4952 Roo.bootstrap.PaginationItem = function(config){
4953 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4958 * The raw click event for the entire grid.
4959 * @param {Roo.EventObject} e
4965 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
4969 preventDefault: true,
4974 getAutoCreate : function(){
4980 href : this.href ? this.href : '#',
4981 html : this.html ? this.html : ''
4991 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4995 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5001 initEvents: function() {
5003 this.el.on('click', this.onClick, this);
5006 onClick : function(e)
5008 Roo.log('PaginationItem on click ');
5009 if(this.preventDefault){
5017 this.fireEvent('click', this, e);
5033 * @class Roo.bootstrap.Slider
5034 * @extends Roo.bootstrap.Component
5035 * Bootstrap Slider class
5038 * Create a new Slider
5039 * @param {Object} config The config object
5042 Roo.bootstrap.Slider = function(config){
5043 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5046 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
5048 getAutoCreate : function(){
5052 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5056 cls: 'ui-slider-handle ui-state-default ui-corner-all'
5068 * Ext JS Library 1.1.1
5069 * Copyright(c) 2006-2007, Ext JS, LLC.
5071 * Originally Released Under LGPL - original licence link has changed is not relivant.
5074 * <script type="text/javascript">
5079 * @class Roo.grid.ColumnModel
5080 * @extends Roo.util.Observable
5081 * This is the default implementation of a ColumnModel used by the Grid. It defines
5082 * the columns in the grid.
5085 var colModel = new Roo.grid.ColumnModel([
5086 {header: "Ticker", width: 60, sortable: true, locked: true},
5087 {header: "Company Name", width: 150, sortable: true},
5088 {header: "Market Cap.", width: 100, sortable: true},
5089 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5090 {header: "Employees", width: 100, sortable: true, resizable: false}
5095 * The config options listed for this class are options which may appear in each
5096 * individual column definition.
5097 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5099 * @param {Object} config An Array of column config objects. See this class's
5100 * config objects for details.
5102 Roo.grid.ColumnModel = function(config){
5104 * The config passed into the constructor
5106 this.config = config;
5109 // if no id, create one
5110 // if the column does not have a dataIndex mapping,
5111 // map it to the order it is in the config
5112 for(var i = 0, len = config.length; i < len; i++){
5114 if(typeof c.dataIndex == "undefined"){
5117 if(typeof c.renderer == "string"){
5118 c.renderer = Roo.util.Format[c.renderer];
5120 if(typeof c.id == "undefined"){
5123 if(c.editor && c.editor.xtype){
5124 c.editor = Roo.factory(c.editor, Roo.grid);
5126 if(c.editor && c.editor.isFormField){
5127 c.editor = new Roo.grid.GridEditor(c.editor);
5129 this.lookup[c.id] = c;
5133 * The width of columns which have no width specified (defaults to 100)
5136 this.defaultWidth = 100;
5139 * Default sortable of columns which have no sortable specified (defaults to false)
5142 this.defaultSortable = false;
5146 * @event widthchange
5147 * Fires when the width of a column changes.
5148 * @param {ColumnModel} this
5149 * @param {Number} columnIndex The column index
5150 * @param {Number} newWidth The new width
5152 "widthchange": true,
5154 * @event headerchange
5155 * Fires when the text of a header changes.
5156 * @param {ColumnModel} this
5157 * @param {Number} columnIndex The column index
5158 * @param {Number} newText The new header text
5160 "headerchange": true,
5162 * @event hiddenchange
5163 * Fires when a column is hidden or "unhidden".
5164 * @param {ColumnModel} this
5165 * @param {Number} columnIndex The column index
5166 * @param {Boolean} hidden true if hidden, false otherwise
5168 "hiddenchange": true,
5170 * @event columnmoved
5171 * Fires when a column is moved.
5172 * @param {ColumnModel} this
5173 * @param {Number} oldIndex
5174 * @param {Number} newIndex
5176 "columnmoved" : true,
5178 * @event columlockchange
5179 * Fires when a column's locked state is changed
5180 * @param {ColumnModel} this
5181 * @param {Number} colIndex
5182 * @param {Boolean} locked true if locked
5184 "columnlockchange" : true
5186 Roo.grid.ColumnModel.superclass.constructor.call(this);
5188 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5190 * @cfg {String} header The header text to display in the Grid view.
5193 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5194 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5195 * specified, the column's index is used as an index into the Record's data Array.
5198 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5199 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5202 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5203 * Defaults to the value of the {@link #defaultSortable} property.
5204 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5207 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5210 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5213 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5216 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5219 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5220 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5221 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5222 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5225 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5228 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5231 * @cfg {String} cursor (Optional)
5234 * @cfg {String} tooltip (Optional)
5237 * @cfg {Number} xs (Optional)
5240 * @cfg {Number} sm (Optional)
5243 * @cfg {Number} md (Optional)
5246 * @cfg {Number} lg (Optional)
5249 * Returns the id of the column at the specified index.
5250 * @param {Number} index The column index
5251 * @return {String} the id
5253 getColumnId : function(index){
5254 return this.config[index].id;
5258 * Returns the column for a specified id.
5259 * @param {String} id The column id
5260 * @return {Object} the column
5262 getColumnById : function(id){
5263 return this.lookup[id];
5268 * Returns the column for a specified dataIndex.
5269 * @param {String} dataIndex The column dataIndex
5270 * @return {Object|Boolean} the column or false if not found
5272 getColumnByDataIndex: function(dataIndex){
5273 var index = this.findColumnIndex(dataIndex);
5274 return index > -1 ? this.config[index] : false;
5278 * Returns the index for a specified column id.
5279 * @param {String} id The column id
5280 * @return {Number} the index, or -1 if not found
5282 getIndexById : function(id){
5283 for(var i = 0, len = this.config.length; i < len; i++){
5284 if(this.config[i].id == id){
5292 * Returns the index for a specified column dataIndex.
5293 * @param {String} dataIndex The column dataIndex
5294 * @return {Number} the index, or -1 if not found
5297 findColumnIndex : function(dataIndex){
5298 for(var i = 0, len = this.config.length; i < len; i++){
5299 if(this.config[i].dataIndex == dataIndex){
5307 moveColumn : function(oldIndex, newIndex){
5308 var c = this.config[oldIndex];
5309 this.config.splice(oldIndex, 1);
5310 this.config.splice(newIndex, 0, c);
5311 this.dataMap = null;
5312 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5315 isLocked : function(colIndex){
5316 return this.config[colIndex].locked === true;
5319 setLocked : function(colIndex, value, suppressEvent){
5320 if(this.isLocked(colIndex) == value){
5323 this.config[colIndex].locked = value;
5325 this.fireEvent("columnlockchange", this, colIndex, value);
5329 getTotalLockedWidth : function(){
5331 for(var i = 0; i < this.config.length; i++){
5332 if(this.isLocked(i) && !this.isHidden(i)){
5333 this.totalWidth += this.getColumnWidth(i);
5339 getLockedCount : function(){
5340 for(var i = 0, len = this.config.length; i < len; i++){
5341 if(!this.isLocked(i)){
5346 return this.config.length;
5350 * Returns the number of columns.
5353 getColumnCount : function(visibleOnly){
5354 if(visibleOnly === true){
5356 for(var i = 0, len = this.config.length; i < len; i++){
5357 if(!this.isHidden(i)){
5363 return this.config.length;
5367 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5368 * @param {Function} fn
5369 * @param {Object} scope (optional)
5370 * @return {Array} result
5372 getColumnsBy : function(fn, scope){
5374 for(var i = 0, len = this.config.length; i < len; i++){
5375 var c = this.config[i];
5376 if(fn.call(scope||this, c, i) === true){
5384 * Returns true if the specified column is sortable.
5385 * @param {Number} col The column index
5388 isSortable : function(col){
5389 if(typeof this.config[col].sortable == "undefined"){
5390 return this.defaultSortable;
5392 return this.config[col].sortable;
5396 * Returns the rendering (formatting) function defined for the column.
5397 * @param {Number} col The column index.
5398 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5400 getRenderer : function(col){
5401 if(!this.config[col].renderer){
5402 return Roo.grid.ColumnModel.defaultRenderer;
5404 return this.config[col].renderer;
5408 * Sets the rendering (formatting) function for a column.
5409 * @param {Number} col The column index
5410 * @param {Function} fn The function to use to process the cell's raw data
5411 * to return HTML markup for the grid view. The render function is called with
5412 * the following parameters:<ul>
5413 * <li>Data value.</li>
5414 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5415 * <li>css A CSS style string to apply to the table cell.</li>
5416 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5417 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5418 * <li>Row index</li>
5419 * <li>Column index</li>
5420 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5422 setRenderer : function(col, fn){
5423 this.config[col].renderer = fn;
5427 * Returns the width for the specified column.
5428 * @param {Number} col The column index
5431 getColumnWidth : function(col){
5432 return this.config[col].width * 1 || this.defaultWidth;
5436 * Sets the width for a column.
5437 * @param {Number} col The column index
5438 * @param {Number} width The new width
5440 setColumnWidth : function(col, width, suppressEvent){
5441 this.config[col].width = width;
5442 this.totalWidth = null;
5444 this.fireEvent("widthchange", this, col, width);
5449 * Returns the total width of all columns.
5450 * @param {Boolean} includeHidden True to include hidden column widths
5453 getTotalWidth : function(includeHidden){
5454 if(!this.totalWidth){
5455 this.totalWidth = 0;
5456 for(var i = 0, len = this.config.length; i < len; i++){
5457 if(includeHidden || !this.isHidden(i)){
5458 this.totalWidth += this.getColumnWidth(i);
5462 return this.totalWidth;
5466 * Returns the header for the specified column.
5467 * @param {Number} col The column index
5470 getColumnHeader : function(col){
5471 return this.config[col].header;
5475 * Sets the header for a column.
5476 * @param {Number} col The column index
5477 * @param {String} header The new header
5479 setColumnHeader : function(col, header){
5480 this.config[col].header = header;
5481 this.fireEvent("headerchange", this, col, header);
5485 * Returns the tooltip for the specified column.
5486 * @param {Number} col The column index
5489 getColumnTooltip : function(col){
5490 return this.config[col].tooltip;
5493 * Sets the tooltip for a column.
5494 * @param {Number} col The column index
5495 * @param {String} tooltip The new tooltip
5497 setColumnTooltip : function(col, tooltip){
5498 this.config[col].tooltip = tooltip;
5502 * Returns the dataIndex for the specified column.
5503 * @param {Number} col The column index
5506 getDataIndex : function(col){
5507 return this.config[col].dataIndex;
5511 * Sets the dataIndex for a column.
5512 * @param {Number} col The column index
5513 * @param {Number} dataIndex The new dataIndex
5515 setDataIndex : function(col, dataIndex){
5516 this.config[col].dataIndex = dataIndex;
5522 * Returns true if the cell is editable.
5523 * @param {Number} colIndex The column index
5524 * @param {Number} rowIndex The row index - this is nto actually used..?
5527 isCellEditable : function(colIndex, rowIndex){
5528 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5532 * Returns the editor defined for the cell/column.
5533 * return false or null to disable editing.
5534 * @param {Number} colIndex The column index
5535 * @param {Number} rowIndex The row index
5538 getCellEditor : function(colIndex, rowIndex){
5539 return this.config[colIndex].editor;
5543 * Sets if a column is editable.
5544 * @param {Number} col The column index
5545 * @param {Boolean} editable True if the column is editable
5547 setEditable : function(col, editable){
5548 this.config[col].editable = editable;
5553 * Returns true if the column is hidden.
5554 * @param {Number} colIndex The column index
5557 isHidden : function(colIndex){
5558 return this.config[colIndex].hidden;
5563 * Returns true if the column width cannot be changed
5565 isFixed : function(colIndex){
5566 return this.config[colIndex].fixed;
5570 * Returns true if the column can be resized
5573 isResizable : function(colIndex){
5574 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5577 * Sets if a column is hidden.
5578 * @param {Number} colIndex The column index
5579 * @param {Boolean} hidden True if the column is hidden
5581 setHidden : function(colIndex, hidden){
5582 this.config[colIndex].hidden = hidden;
5583 this.totalWidth = null;
5584 this.fireEvent("hiddenchange", this, colIndex, hidden);
5588 * Sets the editor for a column.
5589 * @param {Number} col The column index
5590 * @param {Object} editor The editor object
5592 setEditor : function(col, editor){
5593 this.config[col].editor = editor;
5597 Roo.grid.ColumnModel.defaultRenderer = function(value)
5599 if(typeof value == "object") {
5602 if(typeof value == "string" && value.length < 1){
5606 return String.format("{0}", value);
5609 // Alias for backwards compatibility
5610 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5613 * Ext JS Library 1.1.1
5614 * Copyright(c) 2006-2007, Ext JS, LLC.
5616 * Originally Released Under LGPL - original licence link has changed is not relivant.
5619 * <script type="text/javascript">
5623 * @class Roo.LoadMask
5624 * A simple utility class for generically masking elements while loading data. If the element being masked has
5625 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5626 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5627 * element's UpdateManager load indicator and will be destroyed after the initial load.
5629 * Create a new LoadMask
5630 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5631 * @param {Object} config The config object
5633 Roo.LoadMask = function(el, config){
5634 this.el = Roo.get(el);
5635 Roo.apply(this, config);
5637 this.store.on('beforeload', this.onBeforeLoad, this);
5638 this.store.on('load', this.onLoad, this);
5639 this.store.on('loadexception', this.onLoadException, this);
5640 this.removeMask = false;
5642 var um = this.el.getUpdateManager();
5643 um.showLoadIndicator = false; // disable the default indicator
5644 um.on('beforeupdate', this.onBeforeLoad, this);
5645 um.on('update', this.onLoad, this);
5646 um.on('failure', this.onLoad, this);
5647 this.removeMask = true;
5651 Roo.LoadMask.prototype = {
5653 * @cfg {Boolean} removeMask
5654 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5655 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5659 * The text to display in a centered loading message box (defaults to 'Loading...')
5663 * @cfg {String} msgCls
5664 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5666 msgCls : 'x-mask-loading',
5669 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5675 * Disables the mask to prevent it from being displayed
5677 disable : function(){
5678 this.disabled = true;
5682 * Enables the mask so that it can be displayed
5684 enable : function(){
5685 this.disabled = false;
5688 onLoadException : function()
5692 if (typeof(arguments[3]) != 'undefined') {
5693 Roo.MessageBox.alert("Error loading",arguments[3]);
5697 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5698 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5705 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5710 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5714 onBeforeLoad : function(){
5716 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5721 destroy : function(){
5723 this.store.un('beforeload', this.onBeforeLoad, this);
5724 this.store.un('load', this.onLoad, this);
5725 this.store.un('loadexception', this.onLoadException, this);
5727 var um = this.el.getUpdateManager();
5728 um.un('beforeupdate', this.onBeforeLoad, this);
5729 um.un('update', this.onLoad, this);
5730 um.un('failure', this.onLoad, this);
5741 * @class Roo.bootstrap.Table
5742 * @extends Roo.bootstrap.Component
5743 * Bootstrap Table class
5744 * @cfg {String} cls table class
5745 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5746 * @cfg {String} bgcolor Specifies the background color for a table
5747 * @cfg {Number} border Specifies whether the table cells should have borders or not
5748 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5749 * @cfg {Number} cellspacing Specifies the space between cells
5750 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5751 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5752 * @cfg {String} sortable Specifies that the table should be sortable
5753 * @cfg {String} summary Specifies a summary of the content of a table
5754 * @cfg {Number} width Specifies the width of a table
5755 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5757 * @cfg {boolean} striped Should the rows be alternative striped
5758 * @cfg {boolean} bordered Add borders to the table
5759 * @cfg {boolean} hover Add hover highlighting
5760 * @cfg {boolean} condensed Format condensed
5761 * @cfg {boolean} responsive Format condensed
5762 * @cfg {Boolean} loadMask (true|false) default false
5763 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5764 * @cfg {Boolean} headerShow (true|false) generate thead, default true
5765 * @cfg {Boolean} rowSelection (true|false) default false
5766 * @cfg {Boolean} cellSelection (true|false) default false
5767 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5768 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
5769 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
5773 * Create a new Table
5774 * @param {Object} config The config object
5777 Roo.bootstrap.Table = function(config){
5778 Roo.bootstrap.Table.superclass.constructor.call(this, config);
5783 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5784 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5785 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5786 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5788 this.sm = this.sm || {xtype: 'RowSelectionModel'};
5790 this.sm.grid = this;
5791 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5792 this.sm = this.selModel;
5793 this.sm.xmodule = this.xmodule || false;
5796 if (this.cm && typeof(this.cm.config) == 'undefined') {
5797 this.colModel = new Roo.grid.ColumnModel(this.cm);
5798 this.cm = this.colModel;
5799 this.cm.xmodule = this.xmodule || false;
5802 this.store= Roo.factory(this.store, Roo.data);
5803 this.ds = this.store;
5804 this.ds.xmodule = this.xmodule || false;
5807 if (this.footer && this.store) {
5808 this.footer.dataSource = this.ds;
5809 this.footer = Roo.factory(this.footer);
5816 * Fires when a cell is clicked
5817 * @param {Roo.bootstrap.Table} this
5818 * @param {Roo.Element} el
5819 * @param {Number} rowIndex
5820 * @param {Number} columnIndex
5821 * @param {Roo.EventObject} e
5825 * @event celldblclick
5826 * Fires when a cell is double clicked
5827 * @param {Roo.bootstrap.Table} this
5828 * @param {Roo.Element} el
5829 * @param {Number} rowIndex
5830 * @param {Number} columnIndex
5831 * @param {Roo.EventObject} e
5833 "celldblclick" : true,
5836 * Fires when a row is clicked
5837 * @param {Roo.bootstrap.Table} this
5838 * @param {Roo.Element} el
5839 * @param {Number} rowIndex
5840 * @param {Roo.EventObject} e
5844 * @event rowdblclick
5845 * Fires when a row is double clicked
5846 * @param {Roo.bootstrap.Table} this
5847 * @param {Roo.Element} el
5848 * @param {Number} rowIndex
5849 * @param {Roo.EventObject} e
5851 "rowdblclick" : true,
5854 * Fires when a mouseover occur
5855 * @param {Roo.bootstrap.Table} this
5856 * @param {Roo.Element} el
5857 * @param {Number} rowIndex
5858 * @param {Number} columnIndex
5859 * @param {Roo.EventObject} e
5864 * Fires when a mouseout occur
5865 * @param {Roo.bootstrap.Table} this
5866 * @param {Roo.Element} el
5867 * @param {Number} rowIndex
5868 * @param {Number} columnIndex
5869 * @param {Roo.EventObject} e
5874 * Fires when a row is rendered, so you can change add a style to it.
5875 * @param {Roo.bootstrap.Table} this
5876 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
5880 * @event rowsrendered
5881 * Fires when all the rows have been rendered
5882 * @param {Roo.bootstrap.Table} this
5884 'rowsrendered' : true,
5886 * @event contextmenu
5887 * The raw contextmenu event for the entire grid.
5888 * @param {Roo.EventObject} e
5890 "contextmenu" : true,
5892 * @event rowcontextmenu
5893 * Fires when a row is right clicked
5894 * @param {Roo.bootstrap.Table} this
5895 * @param {Number} rowIndex
5896 * @param {Roo.EventObject} e
5898 "rowcontextmenu" : true,
5900 * @event cellcontextmenu
5901 * Fires when a cell is right clicked
5902 * @param {Roo.bootstrap.Table} this
5903 * @param {Number} rowIndex
5904 * @param {Number} cellIndex
5905 * @param {Roo.EventObject} e
5907 "cellcontextmenu" : true,
5909 * @event headercontextmenu
5910 * Fires when a header is right clicked
5911 * @param {Roo.bootstrap.Table} this
5912 * @param {Number} columnIndex
5913 * @param {Roo.EventObject} e
5915 "headercontextmenu" : true
5919 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
5945 rowSelection : false,
5946 cellSelection : false,
5949 // Roo.Element - the tbody
5951 // Roo.Element - thead element
5954 container: false, // used by gridpanel...
5958 getAutoCreate : function()
5960 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5967 if (this.scrollBody) {
5968 cfg.cls += ' table-body-fixed';
5971 cfg.cls += ' table-striped';
5975 cfg.cls += ' table-hover';
5977 if (this.bordered) {
5978 cfg.cls += ' table-bordered';
5980 if (this.condensed) {
5981 cfg.cls += ' table-condensed';
5983 if (this.responsive) {
5984 cfg.cls += ' table-responsive';
5988 cfg.cls+= ' ' +this.cls;
5991 // this lot should be simplifed...
5994 cfg.align=this.align;
5997 cfg.bgcolor=this.bgcolor;
6000 cfg.border=this.border;
6002 if (this.cellpadding) {
6003 cfg.cellpadding=this.cellpadding;
6005 if (this.cellspacing) {
6006 cfg.cellspacing=this.cellspacing;
6009 cfg.frame=this.frame;
6012 cfg.rules=this.rules;
6014 if (this.sortable) {
6015 cfg.sortable=this.sortable;
6018 cfg.summary=this.summary;
6021 cfg.width=this.width;
6024 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6027 if(this.store || this.cm){
6028 if(this.headerShow){
6029 cfg.cn.push(this.renderHeader());
6032 cfg.cn.push(this.renderBody());
6034 if(this.footerShow){
6035 cfg.cn.push(this.renderFooter());
6037 // where does this come from?
6038 //cfg.cls+= ' TableGrid';
6041 return { cn : [ cfg ] };
6044 initEvents : function()
6046 if(!this.store || !this.cm){
6049 if (this.selModel) {
6050 this.selModel.initEvents();
6054 //Roo.log('initEvents with ds!!!!');
6056 this.mainBody = this.el.select('tbody', true).first();
6057 this.mainHead = this.el.select('thead', true).first();
6064 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6065 e.on('click', _this.sort, _this);
6068 this.mainBody.on("click", this.onClick, this);
6069 this.mainBody.on("dblclick", this.onDblClick, this);
6071 // why is this done????? = it breaks dialogs??
6072 //this.parent().el.setStyle('position', 'relative');
6076 this.footer.parentId = this.id;
6077 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6080 this.el.select('tfoot tr td').first().addClass('hide');
6084 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6086 this.store.on('load', this.onLoad, this);
6087 this.store.on('beforeload', this.onBeforeLoad, this);
6088 this.store.on('update', this.onUpdate, this);
6089 this.store.on('add', this.onAdd, this);
6090 this.store.on("clear", this.clear, this);
6092 this.el.on("contextmenu", this.onContextMenu, this);
6094 this.mainBody.on('scroll', this.onBodyScroll, this);
6099 onContextMenu : function(e, t)
6101 this.processEvent("contextmenu", e);
6104 processEvent : function(name, e)
6106 if (name != 'touchstart' ) {
6107 this.fireEvent(name, e);
6110 var t = e.getTarget();
6112 var cell = Roo.get(t);
6118 if(cell.findParent('tfoot', false, true)){
6122 if(cell.findParent('thead', false, true)){
6124 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6125 cell = Roo.get(t).findParent('th', false, true);
6127 Roo.log("failed to find th in thead?");
6128 Roo.log(e.getTarget());
6133 var cellIndex = cell.dom.cellIndex;
6135 var ename = name == 'touchstart' ? 'click' : name;
6136 this.fireEvent("header" + ename, this, cellIndex, e);
6141 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6142 cell = Roo.get(t).findParent('td', false, true);
6144 Roo.log("failed to find th in tbody?");
6145 Roo.log(e.getTarget());
6150 var row = cell.findParent('tr', false, true);
6151 var cellIndex = cell.dom.cellIndex;
6152 var rowIndex = row.dom.rowIndex - 1;
6156 this.fireEvent("row" + name, this, rowIndex, e);
6160 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6166 onMouseover : function(e, el)
6168 var cell = Roo.get(el);
6174 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6175 cell = cell.findParent('td', false, true);
6178 var row = cell.findParent('tr', false, true);
6179 var cellIndex = cell.dom.cellIndex;
6180 var rowIndex = row.dom.rowIndex - 1; // start from 0
6182 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6186 onMouseout : function(e, el)
6188 var cell = Roo.get(el);
6194 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6195 cell = cell.findParent('td', false, true);
6198 var row = cell.findParent('tr', false, true);
6199 var cellIndex = cell.dom.cellIndex;
6200 var rowIndex = row.dom.rowIndex - 1; // start from 0
6202 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6206 onClick : function(e, el)
6208 var cell = Roo.get(el);
6210 if(!cell || (!this.cellSelection && !this.rowSelection)){
6214 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6215 cell = cell.findParent('td', false, true);
6218 if(!cell || typeof(cell) == 'undefined'){
6222 var row = cell.findParent('tr', false, true);
6224 if(!row || typeof(row) == 'undefined'){
6228 var cellIndex = cell.dom.cellIndex;
6229 var rowIndex = this.getRowIndex(row);
6231 // why??? - should these not be based on SelectionModel?
6232 if(this.cellSelection){
6233 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6236 if(this.rowSelection){
6237 this.fireEvent('rowclick', this, row, rowIndex, e);
6243 onDblClick : function(e,el)
6245 var cell = Roo.get(el);
6247 if(!cell || (!this.cellSelection && !this.rowSelection)){
6251 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6252 cell = cell.findParent('td', false, true);
6255 if(!cell || typeof(cell) == 'undefined'){
6259 var row = cell.findParent('tr', false, true);
6261 if(!row || typeof(row) == 'undefined'){
6265 var cellIndex = cell.dom.cellIndex;
6266 var rowIndex = this.getRowIndex(row);
6268 if(this.cellSelection){
6269 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6272 if(this.rowSelection){
6273 this.fireEvent('rowdblclick', this, row, rowIndex, e);
6277 sort : function(e,el)
6279 var col = Roo.get(el);
6281 if(!col.hasClass('sortable')){
6285 var sort = col.attr('sort');
6288 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6292 this.store.sortInfo = {field : sort, direction : dir};
6295 Roo.log("calling footer first");
6296 this.footer.onClick('first');
6299 this.store.load({ params : { start : 0 } });
6303 renderHeader : function()
6311 this.totalWidth = 0;
6313 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6315 var config = cm.config[i];
6320 html: cm.getColumnHeader(i)
6325 if(typeof(config.sortable) != 'undefined' && config.sortable){
6327 c.html = '<i class="glyphicon"></i>' + c.html;
6330 if(typeof(config.lgHeader) != 'undefined'){
6331 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6334 if(typeof(config.mdHeader) != 'undefined'){
6335 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6338 if(typeof(config.smHeader) != 'undefined'){
6339 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6342 if(typeof(config.xsHeader) != 'undefined'){
6343 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6350 if(typeof(config.tooltip) != 'undefined'){
6351 c.tooltip = config.tooltip;
6354 if(typeof(config.colspan) != 'undefined'){
6355 c.colspan = config.colspan;
6358 if(typeof(config.hidden) != 'undefined' && config.hidden){
6359 c.style += ' display:none;';
6362 if(typeof(config.dataIndex) != 'undefined'){
6363 c.sort = config.dataIndex;
6368 if(typeof(config.align) != 'undefined' && config.align.length){
6369 c.style += ' text-align:' + config.align + ';';
6372 if(typeof(config.width) != 'undefined'){
6373 c.style += ' width:' + config.width + 'px;';
6374 this.totalWidth += config.width;
6376 this.totalWidth += 100; // assume minimum of 100 per column?
6379 if(typeof(config.cls) != 'undefined'){
6380 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6383 ['xs','sm','md','lg'].map(function(size){
6385 if(typeof(config[size]) == 'undefined'){
6389 if (!config[size]) { // 0 = hidden
6390 c.cls += ' hidden-' + size;
6394 c.cls += ' col-' + size + '-' + config[size];
6404 renderBody : function()
6414 colspan : this.cm.getColumnCount()
6424 renderFooter : function()
6434 colspan : this.cm.getColumnCount()
6448 // Roo.log('ds onload');
6453 var ds = this.store;
6455 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6456 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6457 if (_this.store.sortInfo) {
6459 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6460 e.select('i', true).addClass(['glyphicon-arrow-up']);
6463 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6464 e.select('i', true).addClass(['glyphicon-arrow-down']);
6469 var tbody = this.mainBody;
6471 if(ds.getCount() > 0){
6472 ds.data.each(function(d,rowIndex){
6473 var row = this.renderRow(cm, ds, rowIndex);
6475 tbody.createChild(row);
6479 if(row.cellObjects.length){
6480 Roo.each(row.cellObjects, function(r){
6481 _this.renderCellObject(r);
6488 Roo.each(this.el.select('tbody td', true).elements, function(e){
6489 e.on('mouseover', _this.onMouseover, _this);
6492 Roo.each(this.el.select('tbody td', true).elements, function(e){
6493 e.on('mouseout', _this.onMouseout, _this);
6495 this.fireEvent('rowsrendered', this);
6496 //if(this.loadMask){
6497 // this.maskEl.hide();
6504 onUpdate : function(ds,record)
6506 this.refreshRow(record);
6510 onRemove : function(ds, record, index, isUpdate){
6511 if(isUpdate !== true){
6512 this.fireEvent("beforerowremoved", this, index, record);
6514 var bt = this.mainBody.dom;
6516 var rows = this.el.select('tbody > tr', true).elements;
6518 if(typeof(rows[index]) != 'undefined'){
6519 bt.removeChild(rows[index].dom);
6522 // if(bt.rows[index]){
6523 // bt.removeChild(bt.rows[index]);
6526 if(isUpdate !== true){
6527 //this.stripeRows(index);
6528 //this.syncRowHeights(index, index);
6530 this.fireEvent("rowremoved", this, index, record);
6534 onAdd : function(ds, records, rowIndex)
6536 //Roo.log('on Add called');
6537 // - note this does not handle multiple adding very well..
6538 var bt = this.mainBody.dom;
6539 for (var i =0 ; i < records.length;i++) {
6540 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6541 //Roo.log(records[i]);
6542 //Roo.log(this.store.getAt(rowIndex+i));
6543 this.insertRow(this.store, rowIndex + i, false);
6550 refreshRow : function(record){
6551 var ds = this.store, index;
6552 if(typeof record == 'number'){
6554 record = ds.getAt(index);
6556 index = ds.indexOf(record);
6558 this.insertRow(ds, index, true);
6560 this.onRemove(ds, record, index+1, true);
6562 //this.syncRowHeights(index, index);
6564 this.fireEvent("rowupdated", this, index, record);
6567 insertRow : function(dm, rowIndex, isUpdate){
6570 this.fireEvent("beforerowsinserted", this, rowIndex);
6572 //var s = this.getScrollState();
6573 var row = this.renderRow(this.cm, this.store, rowIndex);
6574 // insert before rowIndex..
6575 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6579 if(row.cellObjects.length){
6580 Roo.each(row.cellObjects, function(r){
6581 _this.renderCellObject(r);
6586 this.fireEvent("rowsinserted", this, rowIndex);
6587 //this.syncRowHeights(firstRow, lastRow);
6588 //this.stripeRows(firstRow);
6595 getRowDom : function(rowIndex)
6597 var rows = this.el.select('tbody > tr', true).elements;
6599 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6602 // returns the object tree for a tr..
6605 renderRow : function(cm, ds, rowIndex)
6608 var d = ds.getAt(rowIndex);
6615 var cellObjects = [];
6617 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6618 var config = cm.config[i];
6620 var renderer = cm.getRenderer(i);
6624 if(typeof(renderer) !== 'undefined'){
6625 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6627 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6628 // and are rendered into the cells after the row is rendered - using the id for the element.
6630 if(typeof(value) === 'object'){
6640 rowIndex : rowIndex,
6645 this.fireEvent('rowclass', this, rowcfg);
6649 cls : rowcfg.rowClass,
6651 html: (typeof(value) === 'object') ? '' : value
6658 if(typeof(config.colspan) != 'undefined'){
6659 td.colspan = config.colspan;
6662 if(typeof(config.hidden) != 'undefined' && config.hidden){
6663 td.style += ' display:none;';
6666 if(typeof(config.align) != 'undefined' && config.align.length){
6667 td.style += ' text-align:' + config.align + ';';
6670 if(typeof(config.width) != 'undefined'){
6671 td.style += ' width:' + config.width + 'px;';
6674 if(typeof(config.cursor) != 'undefined'){
6675 td.style += ' cursor:' + config.cursor + ';';
6678 if(typeof(config.cls) != 'undefined'){
6679 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6682 ['xs','sm','md','lg'].map(function(size){
6684 if(typeof(config[size]) == 'undefined'){
6688 if (!config[size]) { // 0 = hidden
6689 td.cls += ' hidden-' + size;
6693 td.cls += ' col-' + size + '-' + config[size];
6701 row.cellObjects = cellObjects;
6709 onBeforeLoad : function()
6711 //Roo.log('ds onBeforeLoad');
6715 //if(this.loadMask){
6716 // this.maskEl.show();
6724 this.el.select('tbody', true).first().dom.innerHTML = '';
6727 * Show or hide a row.
6728 * @param {Number} rowIndex to show or hide
6729 * @param {Boolean} state hide
6731 setRowVisibility : function(rowIndex, state)
6733 var bt = this.mainBody.dom;
6735 var rows = this.el.select('tbody > tr', true).elements;
6737 if(typeof(rows[rowIndex]) == 'undefined'){
6740 rows[rowIndex].dom.style.display = state ? '' : 'none';
6744 getSelectionModel : function(){
6746 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6748 return this.selModel;
6751 * Render the Roo.bootstrap object from renderder
6753 renderCellObject : function(r)
6757 var t = r.cfg.render(r.container);
6760 Roo.each(r.cfg.cn, function(c){
6762 container: t.getChildContainer(),
6765 _this.renderCellObject(child);
6770 getRowIndex : function(row)
6774 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6785 * Returns the grid's underlying element = used by panel.Grid
6786 * @return {Element} The element
6788 getGridEl : function(){
6792 * Forces a resize - used by panel.Grid
6793 * @return {Element} The element
6795 autoSize : function()
6797 //var ctr = Roo.get(this.container.dom.parentElement);
6798 var ctr = Roo.get(this.el.dom);
6800 var thd = this.getGridEl().select('thead',true).first();
6801 var tbd = this.getGridEl().select('tbody', true).first();
6802 var tfd = this.getGridEl().select('tfoot', true).first();
6804 var cw = ctr.getWidth();
6808 tbd.setSize(ctr.getWidth(),
6809 ctr.getHeight() - (thd.getHeight() + (tfd ? tfd.getHeight() : 0))
6811 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6814 cw = Math.max(cw, this.totalWidth);
6815 this.getGridEl().select('tr',true).setWidth(cw);
6816 // resize 'expandable coloumn?
6818 return; // we doe not have a view in this design..
6821 onBodyScroll: function()
6823 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6824 this.mainHead.setStyle({
6825 'position' : 'relative',
6826 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6831 var scrollHeight = this.mainBody.dom.scrollHeight;
6833 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
6835 var height = this.mainBody.getHeight();
6837 if(scrollHeight - height == scrollTop) {
6839 var total = this.ds.getTotalCount();
6841 if(this.footer.cursor + this.footer.pageSize < total){
6843 this.footer.ds.load({
6845 start : this.footer.cursor + this.footer.pageSize,
6846 limit : this.footer.pageSize
6867 * @class Roo.bootstrap.TableCell
6868 * @extends Roo.bootstrap.Component
6869 * Bootstrap TableCell class
6870 * @cfg {String} html cell contain text
6871 * @cfg {String} cls cell class
6872 * @cfg {String} tag cell tag (td|th) default td
6873 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6874 * @cfg {String} align Aligns the content in a cell
6875 * @cfg {String} axis Categorizes cells
6876 * @cfg {String} bgcolor Specifies the background color of a cell
6877 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6878 * @cfg {Number} colspan Specifies the number of columns a cell should span
6879 * @cfg {String} headers Specifies one or more header cells a cell is related to
6880 * @cfg {Number} height Sets the height of a cell
6881 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6882 * @cfg {Number} rowspan Sets the number of rows a cell should span
6883 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6884 * @cfg {String} valign Vertical aligns the content in a cell
6885 * @cfg {Number} width Specifies the width of a cell
6888 * Create a new TableCell
6889 * @param {Object} config The config object
6892 Roo.bootstrap.TableCell = function(config){
6893 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6896 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
6916 getAutoCreate : function(){
6917 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6937 cfg.align=this.align
6943 cfg.bgcolor=this.bgcolor
6946 cfg.charoff=this.charoff
6949 cfg.colspan=this.colspan
6952 cfg.headers=this.headers
6955 cfg.height=this.height
6958 cfg.nowrap=this.nowrap
6961 cfg.rowspan=this.rowspan
6964 cfg.scope=this.scope
6967 cfg.valign=this.valign
6970 cfg.width=this.width
6989 * @class Roo.bootstrap.TableRow
6990 * @extends Roo.bootstrap.Component
6991 * Bootstrap TableRow class
6992 * @cfg {String} cls row class
6993 * @cfg {String} align Aligns the content in a table row
6994 * @cfg {String} bgcolor Specifies a background color for a table row
6995 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6996 * @cfg {String} valign Vertical aligns the content in a table row
6999 * Create a new TableRow
7000 * @param {Object} config The config object
7003 Roo.bootstrap.TableRow = function(config){
7004 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7007 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
7015 getAutoCreate : function(){
7016 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7026 cfg.align = this.align;
7029 cfg.bgcolor = this.bgcolor;
7032 cfg.charoff = this.charoff;
7035 cfg.valign = this.valign;
7053 * @class Roo.bootstrap.TableBody
7054 * @extends Roo.bootstrap.Component
7055 * Bootstrap TableBody class
7056 * @cfg {String} cls element class
7057 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7058 * @cfg {String} align Aligns the content inside the element
7059 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7060 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7063 * Create a new TableBody
7064 * @param {Object} config The config object
7067 Roo.bootstrap.TableBody = function(config){
7068 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7071 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
7079 getAutoCreate : function(){
7080 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7094 cfg.align = this.align;
7097 cfg.charoff = this.charoff;
7100 cfg.valign = this.valign;
7107 // initEvents : function()
7114 // this.store = Roo.factory(this.store, Roo.data);
7115 // this.store.on('load', this.onLoad, this);
7117 // this.store.load();
7121 // onLoad: function ()
7123 // this.fireEvent('load', this);
7133 * Ext JS Library 1.1.1
7134 * Copyright(c) 2006-2007, Ext JS, LLC.
7136 * Originally Released Under LGPL - original licence link has changed is not relivant.
7139 * <script type="text/javascript">
7142 // as we use this in bootstrap.
7143 Roo.namespace('Roo.form');
7145 * @class Roo.form.Action
7146 * Internal Class used to handle form actions
7148 * @param {Roo.form.BasicForm} el The form element or its id
7149 * @param {Object} config Configuration options
7154 // define the action interface
7155 Roo.form.Action = function(form, options){
7157 this.options = options || {};
7160 * Client Validation Failed
7163 Roo.form.Action.CLIENT_INVALID = 'client';
7165 * Server Validation Failed
7168 Roo.form.Action.SERVER_INVALID = 'server';
7170 * Connect to Server Failed
7173 Roo.form.Action.CONNECT_FAILURE = 'connect';
7175 * Reading Data from Server Failed
7178 Roo.form.Action.LOAD_FAILURE = 'load';
7180 Roo.form.Action.prototype = {
7182 failureType : undefined,
7183 response : undefined,
7187 run : function(options){
7192 success : function(response){
7197 handleResponse : function(response){
7201 // default connection failure
7202 failure : function(response){
7204 this.response = response;
7205 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7206 this.form.afterAction(this, false);
7209 processResponse : function(response){
7210 this.response = response;
7211 if(!response.responseText){
7214 this.result = this.handleResponse(response);
7218 // utility functions used internally
7219 getUrl : function(appendParams){
7220 var url = this.options.url || this.form.url || this.form.el.dom.action;
7222 var p = this.getParams();
7224 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7230 getMethod : function(){
7231 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7234 getParams : function(){
7235 var bp = this.form.baseParams;
7236 var p = this.options.params;
7238 if(typeof p == "object"){
7239 p = Roo.urlEncode(Roo.applyIf(p, bp));
7240 }else if(typeof p == 'string' && bp){
7241 p += '&' + Roo.urlEncode(bp);
7244 p = Roo.urlEncode(bp);
7249 createCallback : function(){
7251 success: this.success,
7252 failure: this.failure,
7254 timeout: (this.form.timeout*1000),
7255 upload: this.form.fileUpload ? this.success : undefined
7260 Roo.form.Action.Submit = function(form, options){
7261 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7264 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7267 haveProgress : false,
7268 uploadComplete : false,
7270 // uploadProgress indicator.
7271 uploadProgress : function()
7273 if (!this.form.progressUrl) {
7277 if (!this.haveProgress) {
7278 Roo.MessageBox.progress("Uploading", "Uploading");
7280 if (this.uploadComplete) {
7281 Roo.MessageBox.hide();
7285 this.haveProgress = true;
7287 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7289 var c = new Roo.data.Connection();
7291 url : this.form.progressUrl,
7296 success : function(req){
7297 //console.log(data);
7301 rdata = Roo.decode(req.responseText)
7303 Roo.log("Invalid data from server..");
7307 if (!rdata || !rdata.success) {
7309 Roo.MessageBox.alert(Roo.encode(rdata));
7312 var data = rdata.data;
7314 if (this.uploadComplete) {
7315 Roo.MessageBox.hide();
7320 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7321 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7324 this.uploadProgress.defer(2000,this);
7327 failure: function(data) {
7328 Roo.log('progress url failed ');
7339 // run get Values on the form, so it syncs any secondary forms.
7340 this.form.getValues();
7342 var o = this.options;
7343 var method = this.getMethod();
7344 var isPost = method == 'POST';
7345 if(o.clientValidation === false || this.form.isValid()){
7347 if (this.form.progressUrl) {
7348 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7349 (new Date() * 1) + '' + Math.random());
7354 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7355 form:this.form.el.dom,
7356 url:this.getUrl(!isPost),
7358 params:isPost ? this.getParams() : null,
7359 isUpload: this.form.fileUpload
7362 this.uploadProgress();
7364 }else if (o.clientValidation !== false){ // client validation failed
7365 this.failureType = Roo.form.Action.CLIENT_INVALID;
7366 this.form.afterAction(this, false);
7370 success : function(response)
7372 this.uploadComplete= true;
7373 if (this.haveProgress) {
7374 Roo.MessageBox.hide();
7378 var result = this.processResponse(response);
7379 if(result === true || result.success){
7380 this.form.afterAction(this, true);
7384 this.form.markInvalid(result.errors);
7385 this.failureType = Roo.form.Action.SERVER_INVALID;
7387 this.form.afterAction(this, false);
7389 failure : function(response)
7391 this.uploadComplete= true;
7392 if (this.haveProgress) {
7393 Roo.MessageBox.hide();
7396 this.response = response;
7397 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7398 this.form.afterAction(this, false);
7401 handleResponse : function(response){
7402 if(this.form.errorReader){
7403 var rs = this.form.errorReader.read(response);
7406 for(var i = 0, len = rs.records.length; i < len; i++) {
7407 var r = rs.records[i];
7411 if(errors.length < 1){
7415 success : rs.success,
7421 ret = Roo.decode(response.responseText);
7425 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7435 Roo.form.Action.Load = function(form, options){
7436 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7437 this.reader = this.form.reader;
7440 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7445 Roo.Ajax.request(Roo.apply(
7446 this.createCallback(), {
7447 method:this.getMethod(),
7448 url:this.getUrl(false),
7449 params:this.getParams()
7453 success : function(response){
7455 var result = this.processResponse(response);
7456 if(result === true || !result.success || !result.data){
7457 this.failureType = Roo.form.Action.LOAD_FAILURE;
7458 this.form.afterAction(this, false);
7461 this.form.clearInvalid();
7462 this.form.setValues(result.data);
7463 this.form.afterAction(this, true);
7466 handleResponse : function(response){
7467 if(this.form.reader){
7468 var rs = this.form.reader.read(response);
7469 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7471 success : rs.success,
7475 return Roo.decode(response.responseText);
7479 Roo.form.Action.ACTION_TYPES = {
7480 'load' : Roo.form.Action.Load,
7481 'submit' : Roo.form.Action.Submit
7490 * @class Roo.bootstrap.Form
7491 * @extends Roo.bootstrap.Component
7492 * Bootstrap Form class
7493 * @cfg {String} method GET | POST (default POST)
7494 * @cfg {String} labelAlign top | left (default top)
7495 * @cfg {String} align left | right - for navbars
7496 * @cfg {Boolean} loadMask load mask when submit (default true)
7501 * @param {Object} config The config object
7505 Roo.bootstrap.Form = function(config){
7506 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7508 Roo.bootstrap.Form.popover.apply();
7512 * @event clientvalidation
7513 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7514 * @param {Form} this
7515 * @param {Boolean} valid true if the form has passed client-side validation
7517 clientvalidation: true,
7519 * @event beforeaction
7520 * Fires before any action is performed. Return false to cancel the action.
7521 * @param {Form} this
7522 * @param {Action} action The action to be performed
7526 * @event actionfailed
7527 * Fires when an action fails.
7528 * @param {Form} this
7529 * @param {Action} action The action that failed
7531 actionfailed : true,
7533 * @event actioncomplete
7534 * Fires when an action is completed.
7535 * @param {Form} this
7536 * @param {Action} action The action that completed
7538 actioncomplete : true
7543 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7546 * @cfg {String} method
7547 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7552 * The URL to use for form actions if one isn't supplied in the action options.
7555 * @cfg {Boolean} fileUpload
7556 * Set to true if this form is a file upload.
7560 * @cfg {Object} baseParams
7561 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7565 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7569 * @cfg {Sting} align (left|right) for navbar forms
7574 activeAction : null,
7577 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7578 * element by passing it or its id or mask the form itself by passing in true.
7581 waitMsgTarget : false,
7586 * @cfg {Boolean} errorMask (true|false) default false
7591 * @cfg {Number} maskOffset Default 100
7595 getAutoCreate : function(){
7599 method : this.method || 'POST',
7600 id : this.id || Roo.id(),
7603 if (this.parent().xtype.match(/^Nav/)) {
7604 cfg.cls = 'navbar-form navbar-' + this.align;
7608 if (this.labelAlign == 'left' ) {
7609 cfg.cls += ' form-horizontal';
7615 initEvents : function()
7617 this.el.on('submit', this.onSubmit, this);
7618 // this was added as random key presses on the form where triggering form submit.
7619 this.el.on('keypress', function(e) {
7620 if (e.getCharCode() != 13) {
7623 // we might need to allow it for textareas.. and some other items.
7624 // check e.getTarget().
7626 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7630 Roo.log("keypress blocked");
7638 onSubmit : function(e){
7643 * Returns true if client-side validation on the form is successful.
7646 isValid : function(){
7647 var items = this.getItems();
7651 items.each(function(f){
7658 if(!target && f.el.isVisible(true)){
7664 if(this.errorMask && !valid){
7665 Roo.bootstrap.Form.popover.mask(this, target);
7672 * Returns true if any fields in this form have changed since their original load.
7675 isDirty : function(){
7677 var items = this.getItems();
7678 items.each(function(f){
7688 * Performs a predefined action (submit or load) or custom actions you define on this form.
7689 * @param {String} actionName The name of the action type
7690 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
7691 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7692 * accept other config options):
7694 Property Type Description
7695 ---------------- --------------- ----------------------------------------------------------------------------------
7696 url String The url for the action (defaults to the form's url)
7697 method String The form method to use (defaults to the form's method, or POST if not defined)
7698 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
7699 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
7700 validate the form on the client (defaults to false)
7702 * @return {BasicForm} this
7704 doAction : function(action, options){
7705 if(typeof action == 'string'){
7706 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7708 if(this.fireEvent('beforeaction', this, action) !== false){
7709 this.beforeAction(action);
7710 action.run.defer(100, action);
7716 beforeAction : function(action){
7717 var o = action.options;
7720 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7722 // not really supported yet.. ??
7724 //if(this.waitMsgTarget === true){
7725 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7726 //}else if(this.waitMsgTarget){
7727 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7728 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7730 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7736 afterAction : function(action, success){
7737 this.activeAction = null;
7738 var o = action.options;
7740 //if(this.waitMsgTarget === true){
7742 //}else if(this.waitMsgTarget){
7743 // this.waitMsgTarget.unmask();
7745 // Roo.MessageBox.updateProgress(1);
7746 // Roo.MessageBox.hide();
7753 Roo.callback(o.success, o.scope, [this, action]);
7754 this.fireEvent('actioncomplete', this, action);
7758 // failure condition..
7759 // we have a scenario where updates need confirming.
7760 // eg. if a locking scenario exists..
7761 // we look for { errors : { needs_confirm : true }} in the response.
7763 (typeof(action.result) != 'undefined') &&
7764 (typeof(action.result.errors) != 'undefined') &&
7765 (typeof(action.result.errors.needs_confirm) != 'undefined')
7768 Roo.log("not supported yet");
7771 Roo.MessageBox.confirm(
7772 "Change requires confirmation",
7773 action.result.errorMsg,
7778 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
7788 Roo.callback(o.failure, o.scope, [this, action]);
7789 // show an error message if no failed handler is set..
7790 if (!this.hasListener('actionfailed')) {
7791 Roo.log("need to add dialog support");
7793 Roo.MessageBox.alert("Error",
7794 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7795 action.result.errorMsg :
7796 "Saving Failed, please check your entries or try again"
7801 this.fireEvent('actionfailed', this, action);
7806 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7807 * @param {String} id The value to search for
7810 findField : function(id){
7811 var items = this.getItems();
7812 var field = items.get(id);
7814 items.each(function(f){
7815 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7822 return field || null;
7825 * Mark fields in this form invalid in bulk.
7826 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7827 * @return {BasicForm} this
7829 markInvalid : function(errors){
7830 if(errors instanceof Array){
7831 for(var i = 0, len = errors.length; i < len; i++){
7832 var fieldError = errors[i];
7833 var f = this.findField(fieldError.id);
7835 f.markInvalid(fieldError.msg);
7841 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7842 field.markInvalid(errors[id]);
7846 //Roo.each(this.childForms || [], function (f) {
7847 // f.markInvalid(errors);
7854 * Set values for fields in this form in bulk.
7855 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7856 * @return {BasicForm} this
7858 setValues : function(values){
7859 if(values instanceof Array){ // array of objects
7860 for(var i = 0, len = values.length; i < len; i++){
7862 var f = this.findField(v.id);
7864 f.setValue(v.value);
7865 if(this.trackResetOnLoad){
7866 f.originalValue = f.getValue();
7870 }else{ // object hash
7873 if(typeof values[id] != 'function' && (field = this.findField(id))){
7875 if (field.setFromData &&
7877 field.displayField &&
7878 // combos' with local stores can
7879 // be queried via setValue()
7880 // to set their value..
7881 (field.store && !field.store.isLocal)
7885 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7886 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7887 field.setFromData(sd);
7890 field.setValue(values[id]);
7894 if(this.trackResetOnLoad){
7895 field.originalValue = field.getValue();
7901 //Roo.each(this.childForms || [], function (f) {
7902 // f.setValues(values);
7909 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7910 * they are returned as an array.
7911 * @param {Boolean} asString
7914 getValues : function(asString){
7915 //if (this.childForms) {
7916 // copy values from the child forms
7917 // Roo.each(this.childForms, function (f) {
7918 // this.setValues(f.getValues());
7924 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7925 if(asString === true){
7928 return Roo.urlDecode(fs);
7932 * Returns the fields in this form as an object with key/value pairs.
7933 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7936 getFieldValues : function(with_hidden)
7938 var items = this.getItems();
7940 items.each(function(f){
7944 var v = f.getValue();
7945 if (f.inputType =='radio') {
7946 if (typeof(ret[f.getName()]) == 'undefined') {
7947 ret[f.getName()] = ''; // empty..
7950 if (!f.el.dom.checked) {
7958 // not sure if this supported any more..
7959 if ((typeof(v) == 'object') && f.getRawValue) {
7960 v = f.getRawValue() ; // dates..
7962 // combo boxes where name != hiddenName...
7963 if (f.name !== false && f.name != '' && f.name != f.getName()) {
7964 ret[f.name] = f.getRawValue();
7966 ret[f.getName()] = v;
7973 * Clears all invalid messages in this form.
7974 * @return {BasicForm} this
7976 clearInvalid : function(){
7977 var items = this.getItems();
7979 items.each(function(f){
7990 * @return {BasicForm} this
7993 var items = this.getItems();
7994 items.each(function(f){
7998 Roo.each(this.childForms || [], function (f) {
8005 getItems : function()
8007 var r=new Roo.util.MixedCollection(false, function(o){
8008 return o.id || (o.id = Roo.id());
8010 var iter = function(el) {
8017 Roo.each(el.items,function(e) {
8034 Roo.apply(Roo.bootstrap.Form, {
8061 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8062 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8063 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8064 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8067 this.maskEl.top.enableDisplayMode("block");
8068 this.maskEl.left.enableDisplayMode("block");
8069 this.maskEl.bottom.enableDisplayMode("block");
8070 this.maskEl.right.enableDisplayMode("block");
8072 this.toolTip = new Roo.bootstrap.Tooltip({
8073 cls : 'roo-form-error-popover',
8075 'left' : ['r-l', [-2,0], 'right'],
8076 'right' : ['l-r', [2,0], 'left'],
8077 'bottom' : ['tl-bl', [0,2], 'top'],
8078 'top' : [ 'bl-tl', [0,-2], 'bottom']
8082 this.toolTip.render(Roo.get(document.body));
8084 this.toolTip.el.enableDisplayMode("block");
8086 Roo.get(document.body).on('click', function(){
8090 this.isApplied = true
8093 mask : function(form, target)
8097 this.target = target;
8099 if(!this.form.errorMask || !target.el){
8103 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8105 var ot = this.target.el.calcOffsetsTo(scrollable);
8107 var scrollTo = ot[1] - this.form.maskOffset;
8109 var maxScroll = Roo.lib.Dom.getDocumentHeight() - Roo.lib.Dom.getViewportHeight();
8111 scrollTo = Math.min(scrollTo, maxScroll);
8113 scrollable.dom.scrollTop = scrollTo;
8115 var box = this.target.el.getBox();
8117 var zIndex = Roo.bootstrap.Modal.zIndex++;
8119 this.maskEl.top.setStyle('position', 'fixed');
8120 this.maskEl.top.setStyle('z-index', zIndex);
8121 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8122 this.maskEl.top.setXY([0, 0]);
8123 this.maskEl.top.show();
8125 this.maskEl.left.setStyle('position', 'fixed');
8126 this.maskEl.left.setStyle('z-index', zIndex);
8127 this.maskEl.left.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8128 this.maskEl.left.setXY([box.right + this.padding, box.y - this.padding]);
8129 this.maskEl.left.show();
8131 this.maskEl.bottom.setStyle('position', 'fixed');
8132 this.maskEl.bottom.setStyle('z-index', zIndex);
8133 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8134 this.maskEl.bottom.setXY([0, box.bottom + this.padding]);
8135 this.maskEl.bottom.show();
8137 this.maskEl.right.setStyle('position', 'fixed');
8138 this.maskEl.right.setStyle('z-index', zIndex);
8139 this.maskEl.right.setSize(box.x - this.padding, box.height + this.padding * 2);
8140 this.maskEl.right.setXY([0, box.y - this.padding]);
8141 this.maskEl.right.show();
8144 this.toolTip.bindEl = this.target.el;
8146 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8148 var tip = this.target.blankText;
8150 if(this.target.getValue() !== '' && this.target.regexText.length){
8151 tip = this.target.regexText;
8154 this.toolTip.show(tip);
8156 this.intervalID = window.setInterval(function() {
8157 Roo.bootstrap.Form.popover.unmask();
8160 window.onwheel = function(){ return false;};
8162 (function(){ this.isMasked = true; }).defer(500, this);
8170 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8174 this.maskEl.top.setStyle('position', 'absolute');
8175 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8176 this.maskEl.top.hide();
8178 this.maskEl.left.setStyle('position', 'absolute');
8179 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8180 this.maskEl.left.hide();
8182 this.maskEl.bottom.setStyle('position', 'absolute');
8183 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8184 this.maskEl.bottom.hide();
8186 this.maskEl.right.setStyle('position', 'absolute');
8187 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8188 this.maskEl.right.hide();
8190 this.toolTip.hide();
8192 this.toolTip.el.hide();
8194 window.onwheel = function(){ return true;};
8196 if(this.intervalID){
8197 window.clearInterval(this.intervalID);
8198 this.intervalID = false;
8201 this.isMasked = false;
8211 * Ext JS Library 1.1.1
8212 * Copyright(c) 2006-2007, Ext JS, LLC.
8214 * Originally Released Under LGPL - original licence link has changed is not relivant.
8217 * <script type="text/javascript">
8220 * @class Roo.form.VTypes
8221 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8224 Roo.form.VTypes = function(){
8225 // closure these in so they are only created once.
8226 var alpha = /^[a-zA-Z_]+$/;
8227 var alphanum = /^[a-zA-Z0-9_]+$/;
8228 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8229 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8231 // All these messages and functions are configurable
8234 * The function used to validate email addresses
8235 * @param {String} value The email address
8237 'email' : function(v){
8238 return email.test(v);
8241 * The error text to display when the email validation function returns false
8244 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8246 * The keystroke filter mask to be applied on email input
8249 'emailMask' : /[a-z0-9_\.\-@]/i,
8252 * The function used to validate URLs
8253 * @param {String} value The URL
8255 'url' : function(v){
8259 * The error text to display when the url validation function returns false
8262 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8265 * The function used to validate alpha values
8266 * @param {String} value The value
8268 'alpha' : function(v){
8269 return alpha.test(v);
8272 * The error text to display when the alpha validation function returns false
8275 'alphaText' : 'This field should only contain letters and _',
8277 * The keystroke filter mask to be applied on alpha input
8280 'alphaMask' : /[a-z_]/i,
8283 * The function used to validate alphanumeric values
8284 * @param {String} value The value
8286 'alphanum' : function(v){
8287 return alphanum.test(v);
8290 * The error text to display when the alphanumeric validation function returns false
8293 'alphanumText' : 'This field should only contain letters, numbers and _',
8295 * The keystroke filter mask to be applied on alphanumeric input
8298 'alphanumMask' : /[a-z0-9_]/i
8308 * @class Roo.bootstrap.Input
8309 * @extends Roo.bootstrap.Component
8310 * Bootstrap Input class
8311 * @cfg {Boolean} disabled is it disabled
8312 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8313 * @cfg {String} name name of the input
8314 * @cfg {string} fieldLabel - the label associated
8315 * @cfg {string} placeholder - placeholder to put in text.
8316 * @cfg {string} before - input group add on before
8317 * @cfg {string} after - input group add on after
8318 * @cfg {string} size - (lg|sm) or leave empty..
8319 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8320 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8321 * @cfg {Number} md colspan out of 12 for computer-sized screens
8322 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8323 * @cfg {string} value default value of the input
8324 * @cfg {Number} labelWidth set the width of label
8325 * @cfg {Number} labellg set the width of label (1-12)
8326 * @cfg {Number} labelmd set the width of label (1-12)
8327 * @cfg {Number} labelsm set the width of label (1-12)
8328 * @cfg {Number} labelxs set the width of label (1-12)
8329 * @cfg {String} labelAlign (top|left)
8330 * @cfg {Boolean} readOnly Specifies that the field should be read-only
8331 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8332 * @cfg {String} indicatorpos (left|right) default left
8334 * @cfg {String} align (left|center|right) Default left
8335 * @cfg {Boolean} forceFeedback (true|false) Default false
8341 * Create a new Input
8342 * @param {Object} config The config object
8345 Roo.bootstrap.Input = function(config){
8347 Roo.bootstrap.Input.superclass.constructor.call(this, config);
8352 * Fires when this field receives input focus.
8353 * @param {Roo.form.Field} this
8358 * Fires when this field loses input focus.
8359 * @param {Roo.form.Field} this
8364 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8365 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8366 * @param {Roo.form.Field} this
8367 * @param {Roo.EventObject} e The event object
8372 * Fires just before the field blurs if the field value has changed.
8373 * @param {Roo.form.Field} this
8374 * @param {Mixed} newValue The new value
8375 * @param {Mixed} oldValue The original value
8380 * Fires after the field has been marked as invalid.
8381 * @param {Roo.form.Field} this
8382 * @param {String} msg The validation message
8387 * Fires after the field has been validated with no errors.
8388 * @param {Roo.form.Field} this
8393 * Fires after the key up
8394 * @param {Roo.form.Field} this
8395 * @param {Roo.EventObject} e The event Object
8401 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
8403 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8404 automatic validation (defaults to "keyup").
8406 validationEvent : "keyup",
8408 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8410 validateOnBlur : true,
8412 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8414 validationDelay : 250,
8416 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8418 focusClass : "x-form-focus", // not needed???
8422 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8424 invalidClass : "has-warning",
8427 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8429 validClass : "has-success",
8432 * @cfg {Boolean} hasFeedback (true|false) default true
8437 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8439 invalidFeedbackClass : "glyphicon-warning-sign",
8442 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8444 validFeedbackClass : "glyphicon-ok",
8447 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8449 selectOnFocus : false,
8452 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8456 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8461 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8463 disableKeyFilter : false,
8466 * @cfg {Boolean} disabled True to disable the field (defaults to false).
8470 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8474 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8476 blankText : "Please complete this mandatory field",
8479 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8483 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8485 maxLength : Number.MAX_VALUE,
8487 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8489 minLengthText : "The minimum length for this field is {0}",
8491 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8493 maxLengthText : "The maximum length for this field is {0}",
8497 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8498 * If available, this function will be called only after the basic validators all return true, and will be passed the
8499 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8503 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8504 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8505 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
8509 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
8513 autocomplete: false,
8532 formatedValue : false,
8533 forceFeedback : false,
8535 indicatorpos : 'left',
8542 parentLabelAlign : function()
8545 while (parent.parent()) {
8546 parent = parent.parent();
8547 if (typeof(parent.labelAlign) !='undefined') {
8548 return parent.labelAlign;
8555 getAutoCreate : function()
8557 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8563 if(this.inputType != 'hidden'){
8564 cfg.cls = 'form-group' //input-group
8570 type : this.inputType,
8572 cls : 'form-control',
8573 placeholder : this.placeholder || '',
8574 autocomplete : this.autocomplete || 'new-password'
8578 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8581 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8582 input.maxLength = this.maxLength;
8585 if (this.disabled) {
8586 input.disabled=true;
8589 if (this.readOnly) {
8590 input.readonly=true;
8594 input.name = this.name;
8598 input.cls += ' input-' + this.size;
8602 ['xs','sm','md','lg'].map(function(size){
8603 if (settings[size]) {
8604 cfg.cls += ' col-' + size + '-' + settings[size];
8608 var inputblock = input;
8612 cls: 'glyphicon form-control-feedback'
8615 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8618 cls : 'has-feedback',
8626 if (this.before || this.after) {
8629 cls : 'input-group',
8633 if (this.before && typeof(this.before) == 'string') {
8635 inputblock.cn.push({
8637 cls : 'roo-input-before input-group-addon',
8641 if (this.before && typeof(this.before) == 'object') {
8642 this.before = Roo.factory(this.before);
8644 inputblock.cn.push({
8646 cls : 'roo-input-before input-group-' +
8647 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8651 inputblock.cn.push(input);
8653 if (this.after && typeof(this.after) == 'string') {
8654 inputblock.cn.push({
8656 cls : 'roo-input-after input-group-addon',
8660 if (this.after && typeof(this.after) == 'object') {
8661 this.after = Roo.factory(this.after);
8663 inputblock.cn.push({
8665 cls : 'roo-input-after input-group-' +
8666 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8670 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8671 inputblock.cls += ' has-feedback';
8672 inputblock.cn.push(feedback);
8676 if (align ==='left' && this.fieldLabel.length) {
8678 cfg.cls += ' roo-form-group-label-left';
8683 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8684 tooltip : 'This field is required'
8689 cls : 'control-label',
8690 html : this.fieldLabel
8701 var labelCfg = cfg.cn[1];
8702 var contentCfg = cfg.cn[2];
8704 if(this.indicatorpos == 'right'){
8709 cls : 'control-label',
8710 html : this.fieldLabel
8715 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8716 tooltip : 'This field is required'
8727 labelCfg = cfg.cn[0];
8728 contentCfg = cfg.cn[2];
8732 if(this.labelWidth > 12){
8733 labelCfg.style = "width: " + this.labelWidth + 'px';
8736 if(this.labelWidth < 13 && this.labelmd == 0){
8737 this.labelmd = this.labelWidth;
8740 if(this.labellg > 0){
8741 labelCfg.cls += ' col-lg-' + this.labellg;
8742 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
8745 if(this.labelmd > 0){
8746 labelCfg.cls += ' col-md-' + this.labelmd;
8747 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
8750 if(this.labelsm > 0){
8751 labelCfg.cls += ' col-sm-' + this.labelsm;
8752 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
8755 if(this.labelxs > 0){
8756 labelCfg.cls += ' col-xs-' + this.labelxs;
8757 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
8761 } else if ( this.fieldLabel.length) {
8766 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8767 tooltip : 'This field is required'
8771 //cls : 'input-group-addon',
8772 html : this.fieldLabel
8780 if(this.indicatorpos == 'right'){
8785 //cls : 'input-group-addon',
8786 html : this.fieldLabel
8791 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8792 tooltip : 'This field is required'
8812 if (this.parentType === 'Navbar' && this.parent().bar) {
8813 cfg.cls += ' navbar-form';
8816 if (this.parentType === 'NavGroup') {
8817 cfg.cls += ' navbar-form';
8825 * return the real input element.
8827 inputEl: function ()
8829 return this.el.select('input.form-control',true).first();
8832 tooltipEl : function()
8834 return this.inputEl();
8837 indicatorEl : function()
8839 var indicator = this.el.select('i.roo-required-indicator',true).first();
8849 setDisabled : function(v)
8851 var i = this.inputEl().dom;
8853 i.removeAttribute('disabled');
8857 i.setAttribute('disabled','true');
8859 initEvents : function()
8862 this.inputEl().on("keydown" , this.fireKey, this);
8863 this.inputEl().on("focus", this.onFocus, this);
8864 this.inputEl().on("blur", this.onBlur, this);
8866 this.inputEl().relayEvent('keyup', this);
8868 this.indicator = this.indicatorEl();
8871 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
8872 this.indicator.hide();
8875 // reference to original value for reset
8876 this.originalValue = this.getValue();
8877 //Roo.form.TextField.superclass.initEvents.call(this);
8878 if(this.validationEvent == 'keyup'){
8879 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8880 this.inputEl().on('keyup', this.filterValidation, this);
8882 else if(this.validationEvent !== false){
8883 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8886 if(this.selectOnFocus){
8887 this.on("focus", this.preFocus, this);
8890 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8891 this.inputEl().on("keypress", this.filterKeys, this);
8893 this.inputEl().relayEvent('keypress', this);
8896 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
8897 this.el.on("click", this.autoSize, this);
8900 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8901 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8904 if (typeof(this.before) == 'object') {
8905 this.before.render(this.el.select('.roo-input-before',true).first());
8907 if (typeof(this.after) == 'object') {
8908 this.after.render(this.el.select('.roo-input-after',true).first());
8913 filterValidation : function(e){
8914 if(!e.isNavKeyPress()){
8915 this.validationTask.delay(this.validationDelay);
8919 * Validates the field value
8920 * @return {Boolean} True if the value is valid, else false
8922 validate : function(){
8923 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8924 if(this.disabled || this.validateValue(this.getRawValue())){
8935 * Validates a value according to the field's validation rules and marks the field as invalid
8936 * if the validation fails
8937 * @param {Mixed} value The value to validate
8938 * @return {Boolean} True if the value is valid, else false
8940 validateValue : function(value){
8941 if(value.length < 1) { // if it's blank
8942 if(this.allowBlank){
8948 if(value.length < this.minLength){
8951 if(value.length > this.maxLength){
8955 var vt = Roo.form.VTypes;
8956 if(!vt[this.vtype](value, this)){
8960 if(typeof this.validator == "function"){
8961 var msg = this.validator(value);
8967 if(this.regex && !this.regex.test(value)){
8977 fireKey : function(e){
8978 //Roo.log('field ' + e.getKey());
8979 if(e.isNavKeyPress()){
8980 this.fireEvent("specialkey", this, e);
8983 focus : function (selectText){
8985 this.inputEl().focus();
8986 if(selectText === true){
8987 this.inputEl().dom.select();
8993 onFocus : function(){
8994 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8995 // this.el.addClass(this.focusClass);
8998 this.hasFocus = true;
8999 this.startValue = this.getValue();
9000 this.fireEvent("focus", this);
9004 beforeBlur : Roo.emptyFn,
9008 onBlur : function(){
9010 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9011 //this.el.removeClass(this.focusClass);
9013 this.hasFocus = false;
9014 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9017 var v = this.getValue();
9018 if(String(v) !== String(this.startValue)){
9019 this.fireEvent('change', this, v, this.startValue);
9021 this.fireEvent("blur", this);
9025 * Resets the current field value to the originally loaded value and clears any validation messages
9028 this.setValue(this.originalValue);
9032 * Returns the name of the field
9033 * @return {Mixed} name The name field
9035 getName: function(){
9039 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
9040 * @return {Mixed} value The field value
9042 getValue : function(){
9044 var v = this.inputEl().getValue();
9049 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
9050 * @return {Mixed} value The field value
9052 getRawValue : function(){
9053 var v = this.inputEl().getValue();
9059 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
9060 * @param {Mixed} value The value to set
9062 setRawValue : function(v){
9063 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9066 selectText : function(start, end){
9067 var v = this.getRawValue();
9069 start = start === undefined ? 0 : start;
9070 end = end === undefined ? v.length : end;
9071 var d = this.inputEl().dom;
9072 if(d.setSelectionRange){
9073 d.setSelectionRange(start, end);
9074 }else if(d.createTextRange){
9075 var range = d.createTextRange();
9076 range.moveStart("character", start);
9077 range.moveEnd("character", v.length-end);
9084 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
9085 * @param {Mixed} value The value to set
9087 setValue : function(v){
9090 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9096 processValue : function(value){
9097 if(this.stripCharsRe){
9098 var newValue = value.replace(this.stripCharsRe, '');
9099 if(newValue !== value){
9100 this.setRawValue(newValue);
9107 preFocus : function(){
9109 if(this.selectOnFocus){
9110 this.inputEl().dom.select();
9113 filterKeys : function(e){
9115 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9118 var c = e.getCharCode(), cc = String.fromCharCode(c);
9119 if(Roo.isIE && (e.isSpecialKey() || !cc)){
9122 if(!this.maskRe.test(cc)){
9127 * Clear any invalid styles/messages for this field
9129 clearInvalid : function(){
9131 if(!this.el || this.preventMark){ // not rendered
9136 this.el.removeClass(this.invalidClass);
9138 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9140 var feedback = this.el.select('.form-control-feedback', true).first();
9143 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9148 this.fireEvent('valid', this);
9152 * Mark this field as valid
9154 markValid : function()
9156 if(!this.el || this.preventMark){ // not rendered...
9160 this.el.removeClass([this.invalidClass, this.validClass]);
9162 var feedback = this.el.select('.form-control-feedback', true).first();
9165 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9172 if(this.allowBlank && !this.getRawValue().length){
9177 this.indicator.hide();
9180 this.el.addClass(this.validClass);
9182 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9184 var feedback = this.el.select('.form-control-feedback', true).first();
9187 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9188 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9193 this.fireEvent('valid', this);
9197 * Mark this field as invalid
9198 * @param {String} msg The validation message
9200 markInvalid : function(msg)
9202 if(!this.el || this.preventMark){ // not rendered
9206 this.el.removeClass([this.invalidClass, this.validClass]);
9208 var feedback = this.el.select('.form-control-feedback', true).first();
9211 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9218 if(this.allowBlank && !this.getRawValue().length){
9223 this.indicator.show();
9226 this.el.addClass(this.invalidClass);
9228 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9230 var feedback = this.el.select('.form-control-feedback', true).first();
9233 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9235 if(this.getValue().length || this.forceFeedback){
9236 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9243 this.fireEvent('invalid', this, msg);
9246 SafariOnKeyDown : function(event)
9248 // this is a workaround for a password hang bug on chrome/ webkit.
9249 if (this.inputEl().dom.type != 'password') {
9253 var isSelectAll = false;
9255 if(this.inputEl().dom.selectionEnd > 0){
9256 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9258 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9259 event.preventDefault();
9264 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9266 event.preventDefault();
9267 // this is very hacky as keydown always get's upper case.
9269 var cc = String.fromCharCode(event.getCharCode());
9270 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
9274 adjustWidth : function(tag, w){
9275 tag = tag.toLowerCase();
9276 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9277 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9281 if(tag == 'textarea'){
9284 }else if(Roo.isOpera){
9288 if(tag == 'textarea'){
9307 * @class Roo.bootstrap.TextArea
9308 * @extends Roo.bootstrap.Input
9309 * Bootstrap TextArea class
9310 * @cfg {Number} cols Specifies the visible width of a text area
9311 * @cfg {Number} rows Specifies the visible number of lines in a text area
9312 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9313 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9314 * @cfg {string} html text
9317 * Create a new TextArea
9318 * @param {Object} config The config object
9321 Roo.bootstrap.TextArea = function(config){
9322 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9326 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
9336 getAutoCreate : function(){
9338 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9349 value : this.value || '',
9350 html: this.html || '',
9351 cls : 'form-control',
9352 placeholder : this.placeholder || ''
9356 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9357 input.maxLength = this.maxLength;
9361 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9365 input.cols = this.cols;
9368 if (this.readOnly) {
9369 input.readonly = true;
9373 input.name = this.name;
9377 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9381 ['xs','sm','md','lg'].map(function(size){
9382 if (settings[size]) {
9383 cfg.cls += ' col-' + size + '-' + settings[size];
9387 var inputblock = input;
9389 if(this.hasFeedback && !this.allowBlank){
9393 cls: 'glyphicon form-control-feedback'
9397 cls : 'has-feedback',
9406 if (this.before || this.after) {
9409 cls : 'input-group',
9413 inputblock.cn.push({
9415 cls : 'input-group-addon',
9420 inputblock.cn.push(input);
9422 if(this.hasFeedback && !this.allowBlank){
9423 inputblock.cls += ' has-feedback';
9424 inputblock.cn.push(feedback);
9428 inputblock.cn.push({
9430 cls : 'input-group-addon',
9437 if (align ==='left' && this.fieldLabel.length) {
9442 cls : 'control-label',
9443 html : this.fieldLabel
9454 if(this.labelWidth > 12){
9455 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9458 if(this.labelWidth < 13 && this.labelmd == 0){
9459 this.labelmd = this.labelWidth;
9462 if(this.labellg > 0){
9463 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9464 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9467 if(this.labelmd > 0){
9468 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9469 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9472 if(this.labelsm > 0){
9473 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9474 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9477 if(this.labelxs > 0){
9478 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9479 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9482 } else if ( this.fieldLabel.length) {
9487 //cls : 'input-group-addon',
9488 html : this.fieldLabel
9506 if (this.disabled) {
9507 input.disabled=true;
9514 * return the real textarea element.
9516 inputEl: function ()
9518 return this.el.select('textarea.form-control',true).first();
9522 * Clear any invalid styles/messages for this field
9524 clearInvalid : function()
9527 if(!this.el || this.preventMark){ // not rendered
9531 var label = this.el.select('label', true).first();
9532 var icon = this.el.select('i.fa-star', true).first();
9538 this.el.removeClass(this.invalidClass);
9540 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9542 var feedback = this.el.select('.form-control-feedback', true).first();
9545 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9550 this.fireEvent('valid', this);
9554 * Mark this field as valid
9556 markValid : function()
9558 if(!this.el || this.preventMark){ // not rendered
9562 this.el.removeClass([this.invalidClass, this.validClass]);
9564 var feedback = this.el.select('.form-control-feedback', true).first();
9567 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9570 if(this.disabled || this.allowBlank){
9574 var label = this.el.select('label', true).first();
9575 var icon = this.el.select('i.fa-star', true).first();
9581 this.el.addClass(this.validClass);
9583 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9585 var feedback = this.el.select('.form-control-feedback', true).first();
9588 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9589 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9594 this.fireEvent('valid', this);
9598 * Mark this field as invalid
9599 * @param {String} msg The validation message
9601 markInvalid : function(msg)
9603 if(!this.el || this.preventMark){ // not rendered
9607 this.el.removeClass([this.invalidClass, this.validClass]);
9609 var feedback = this.el.select('.form-control-feedback', true).first();
9612 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9615 if(this.disabled || this.allowBlank){
9619 var label = this.el.select('label', true).first();
9620 var icon = this.el.select('i.fa-star', true).first();
9622 if(!this.getValue().length && label && !icon){
9623 this.el.createChild({
9625 cls : 'text-danger fa fa-lg fa-star',
9626 tooltip : 'This field is required',
9627 style : 'margin-right:5px;'
9631 this.el.addClass(this.invalidClass);
9633 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9635 var feedback = this.el.select('.form-control-feedback', true).first();
9638 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9640 if(this.getValue().length || this.forceFeedback){
9641 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9648 this.fireEvent('invalid', this, msg);
9656 * trigger field - base class for combo..
9661 * @class Roo.bootstrap.TriggerField
9662 * @extends Roo.bootstrap.Input
9663 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9664 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9665 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9666 * for which you can provide a custom implementation. For example:
9668 var trigger = new Roo.bootstrap.TriggerField();
9669 trigger.onTriggerClick = myTriggerFn;
9670 trigger.applyTo('my-field');
9673 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9674 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9675 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
9676 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9677 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9680 * Create a new TriggerField.
9681 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9682 * to the base TextField)
9684 Roo.bootstrap.TriggerField = function(config){
9685 this.mimicing = false;
9686 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9689 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
9691 * @cfg {String} triggerClass A CSS class to apply to the trigger
9694 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9699 * @cfg {Boolean} removable (true|false) special filter default false
9703 /** @cfg {Boolean} grow @hide */
9704 /** @cfg {Number} growMin @hide */
9705 /** @cfg {Number} growMax @hide */
9711 autoSize: Roo.emptyFn,
9718 actionMode : 'wrap',
9723 getAutoCreate : function(){
9725 var align = this.labelAlign || this.parentLabelAlign();
9730 cls: 'form-group' //input-group
9737 type : this.inputType,
9738 cls : 'form-control',
9739 autocomplete: 'new-password',
9740 placeholder : this.placeholder || ''
9744 input.name = this.name;
9747 input.cls += ' input-' + this.size;
9750 if (this.disabled) {
9751 input.disabled=true;
9754 var inputblock = input;
9756 if(this.hasFeedback && !this.allowBlank){
9760 cls: 'glyphicon form-control-feedback'
9763 if(this.removable && !this.editable && !this.tickable){
9765 cls : 'has-feedback',
9771 cls : 'roo-combo-removable-btn close'
9778 cls : 'has-feedback',
9787 if(this.removable && !this.editable && !this.tickable){
9789 cls : 'roo-removable',
9795 cls : 'roo-combo-removable-btn close'
9802 if (this.before || this.after) {
9805 cls : 'input-group',
9809 inputblock.cn.push({
9811 cls : 'input-group-addon',
9816 inputblock.cn.push(input);
9818 if(this.hasFeedback && !this.allowBlank){
9819 inputblock.cls += ' has-feedback';
9820 inputblock.cn.push(feedback);
9824 inputblock.cn.push({
9826 cls : 'input-group-addon',
9839 cls: 'form-hidden-field'
9853 cls: 'form-hidden-field'
9857 cls: 'roo-select2-choices',
9861 cls: 'roo-select2-search-field',
9874 cls: 'roo-select2-container input-group',
9879 // cls: 'typeahead typeahead-long dropdown-menu',
9880 // style: 'display:none'
9885 if(!this.multiple && this.showToggleBtn){
9891 if (this.caret != false) {
9894 cls: 'fa fa-' + this.caret
9901 cls : 'input-group-addon btn dropdown-toggle',
9906 cls: 'combobox-clear',
9920 combobox.cls += ' roo-select2-container-multi';
9923 if (align ==='left' && this.fieldLabel.length) {
9925 cfg.cls += ' roo-form-group-label-left';
9930 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9931 tooltip : 'This field is required'
9936 cls : 'control-label',
9937 html : this.fieldLabel
9949 var labelCfg = cfg.cn[1];
9950 var contentCfg = cfg.cn[2];
9952 if(this.indicatorpos == 'right'){
9957 cls : 'control-label',
9961 html : this.fieldLabel
9965 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9966 tooltip : 'This field is required'
9979 labelCfg = cfg.cn[0];
9980 contentCfg = cfg.cn[1];
9983 if(this.labelWidth > 12){
9984 labelCfg.style = "width: " + this.labelWidth + 'px';
9987 if(this.labelWidth < 13 && this.labelmd == 0){
9988 this.labelmd = this.labelWidth;
9991 if(this.labellg > 0){
9992 labelCfg.cls += ' col-lg-' + this.labellg;
9993 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9996 if(this.labelmd > 0){
9997 labelCfg.cls += ' col-md-' + this.labelmd;
9998 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10001 if(this.labelsm > 0){
10002 labelCfg.cls += ' col-sm-' + this.labelsm;
10003 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10006 if(this.labelxs > 0){
10007 labelCfg.cls += ' col-xs-' + this.labelxs;
10008 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10011 } else if ( this.fieldLabel.length) {
10012 // Roo.log(" label");
10016 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10017 tooltip : 'This field is required'
10021 //cls : 'input-group-addon',
10022 html : this.fieldLabel
10030 if(this.indicatorpos == 'right'){
10038 html : this.fieldLabel
10042 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10043 tooltip : 'This field is required'
10056 // Roo.log(" no label && no align");
10063 ['xs','sm','md','lg'].map(function(size){
10064 if (settings[size]) {
10065 cfg.cls += ' col-' + size + '-' + settings[size];
10076 onResize : function(w, h){
10077 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10078 // if(typeof w == 'number'){
10079 // var x = w - this.trigger.getWidth();
10080 // this.inputEl().setWidth(this.adjustWidth('input', x));
10081 // this.trigger.setStyle('left', x+'px');
10086 adjustSize : Roo.BoxComponent.prototype.adjustSize,
10089 getResizeEl : function(){
10090 return this.inputEl();
10094 getPositionEl : function(){
10095 return this.inputEl();
10099 alignErrorIcon : function(){
10100 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10104 initEvents : function(){
10108 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10109 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10110 if(!this.multiple && this.showToggleBtn){
10111 this.trigger = this.el.select('span.dropdown-toggle',true).first();
10112 if(this.hideTrigger){
10113 this.trigger.setDisplayed(false);
10115 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10119 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10122 if(this.removable && !this.editable && !this.tickable){
10123 var close = this.closeTriggerEl();
10126 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10127 close.on('click', this.removeBtnClick, this, close);
10131 //this.trigger.addClassOnOver('x-form-trigger-over');
10132 //this.trigger.addClassOnClick('x-form-trigger-click');
10135 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10139 closeTriggerEl : function()
10141 var close = this.el.select('.roo-combo-removable-btn', true).first();
10142 return close ? close : false;
10145 removeBtnClick : function(e, h, el)
10147 e.preventDefault();
10149 if(this.fireEvent("remove", this) !== false){
10151 this.fireEvent("afterremove", this)
10155 createList : function()
10157 this.list = Roo.get(document.body).createChild({
10159 cls: 'typeahead typeahead-long dropdown-menu',
10160 style: 'display:none'
10163 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10168 initTrigger : function(){
10173 onDestroy : function(){
10175 this.trigger.removeAllListeners();
10176 // this.trigger.remove();
10179 // this.wrap.remove();
10181 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10185 onFocus : function(){
10186 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10188 if(!this.mimicing){
10189 this.wrap.addClass('x-trigger-wrap-focus');
10190 this.mimicing = true;
10191 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10192 if(this.monitorTab){
10193 this.el.on("keydown", this.checkTab, this);
10200 checkTab : function(e){
10201 if(e.getKey() == e.TAB){
10202 this.triggerBlur();
10207 onBlur : function(){
10212 mimicBlur : function(e, t){
10214 if(!this.wrap.contains(t) && this.validateBlur()){
10215 this.triggerBlur();
10221 triggerBlur : function(){
10222 this.mimicing = false;
10223 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10224 if(this.monitorTab){
10225 this.el.un("keydown", this.checkTab, this);
10227 //this.wrap.removeClass('x-trigger-wrap-focus');
10228 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10232 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10233 validateBlur : function(e, t){
10238 onDisable : function(){
10239 this.inputEl().dom.disabled = true;
10240 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10242 // this.wrap.addClass('x-item-disabled');
10247 onEnable : function(){
10248 this.inputEl().dom.disabled = false;
10249 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10251 // this.el.removeClass('x-item-disabled');
10256 onShow : function(){
10257 var ae = this.getActionEl();
10260 ae.dom.style.display = '';
10261 ae.dom.style.visibility = 'visible';
10267 onHide : function(){
10268 var ae = this.getActionEl();
10269 ae.dom.style.display = 'none';
10273 * The function that should handle the trigger's click event. This method does nothing by default until overridden
10274 * by an implementing function.
10276 * @param {EventObject} e
10278 onTriggerClick : Roo.emptyFn
10282 * Ext JS Library 1.1.1
10283 * Copyright(c) 2006-2007, Ext JS, LLC.
10285 * Originally Released Under LGPL - original licence link has changed is not relivant.
10288 * <script type="text/javascript">
10293 * @class Roo.data.SortTypes
10295 * Defines the default sorting (casting?) comparison functions used when sorting data.
10297 Roo.data.SortTypes = {
10299 * Default sort that does nothing
10300 * @param {Mixed} s The value being converted
10301 * @return {Mixed} The comparison value
10303 none : function(s){
10308 * The regular expression used to strip tags
10312 stripTagsRE : /<\/?[^>]+>/gi,
10315 * Strips all HTML tags to sort on text only
10316 * @param {Mixed} s The value being converted
10317 * @return {String} The comparison value
10319 asText : function(s){
10320 return String(s).replace(this.stripTagsRE, "");
10324 * Strips all HTML tags to sort on text only - Case insensitive
10325 * @param {Mixed} s The value being converted
10326 * @return {String} The comparison value
10328 asUCText : function(s){
10329 return String(s).toUpperCase().replace(this.stripTagsRE, "");
10333 * Case insensitive string
10334 * @param {Mixed} s The value being converted
10335 * @return {String} The comparison value
10337 asUCString : function(s) {
10338 return String(s).toUpperCase();
10343 * @param {Mixed} s The value being converted
10344 * @return {Number} The comparison value
10346 asDate : function(s) {
10350 if(s instanceof Date){
10351 return s.getTime();
10353 return Date.parse(String(s));
10358 * @param {Mixed} s The value being converted
10359 * @return {Float} The comparison value
10361 asFloat : function(s) {
10362 var val = parseFloat(String(s).replace(/,/g, ""));
10371 * @param {Mixed} s The value being converted
10372 * @return {Number} The comparison value
10374 asInt : function(s) {
10375 var val = parseInt(String(s).replace(/,/g, ""));
10383 * Ext JS Library 1.1.1
10384 * Copyright(c) 2006-2007, Ext JS, LLC.
10386 * Originally Released Under LGPL - original licence link has changed is not relivant.
10389 * <script type="text/javascript">
10393 * @class Roo.data.Record
10394 * Instances of this class encapsulate both record <em>definition</em> information, and record
10395 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10396 * to access Records cached in an {@link Roo.data.Store} object.<br>
10398 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10399 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10402 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10404 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10405 * {@link #create}. The parameters are the same.
10406 * @param {Array} data An associative Array of data values keyed by the field name.
10407 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10408 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10409 * not specified an integer id is generated.
10411 Roo.data.Record = function(data, id){
10412 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10417 * Generate a constructor for a specific record layout.
10418 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10419 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10420 * Each field definition object may contain the following properties: <ul>
10421 * <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,
10422 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10423 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10424 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10425 * is being used, then this is a string containing the javascript expression to reference the data relative to
10426 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10427 * to the data item relative to the record element. If the mapping expression is the same as the field name,
10428 * this may be omitted.</p></li>
10429 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10430 * <ul><li>auto (Default, implies no conversion)</li>
10435 * <li>date</li></ul></p></li>
10436 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10437 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10438 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10439 * by the Reader into an object that will be stored in the Record. It is passed the
10440 * following parameters:<ul>
10441 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10443 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10445 * <br>usage:<br><pre><code>
10446 var TopicRecord = Roo.data.Record.create(
10447 {name: 'title', mapping: 'topic_title'},
10448 {name: 'author', mapping: 'username'},
10449 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10450 {name: 'lastPost', mapping: 'post_time', type: 'date'},
10451 {name: 'lastPoster', mapping: 'user2'},
10452 {name: 'excerpt', mapping: 'post_text'}
10455 var myNewRecord = new TopicRecord({
10456 title: 'Do my job please',
10459 lastPost: new Date(),
10460 lastPoster: 'Animal',
10461 excerpt: 'No way dude!'
10463 myStore.add(myNewRecord);
10468 Roo.data.Record.create = function(o){
10469 var f = function(){
10470 f.superclass.constructor.apply(this, arguments);
10472 Roo.extend(f, Roo.data.Record);
10473 var p = f.prototype;
10474 p.fields = new Roo.util.MixedCollection(false, function(field){
10477 for(var i = 0, len = o.length; i < len; i++){
10478 p.fields.add(new Roo.data.Field(o[i]));
10480 f.getField = function(name){
10481 return p.fields.get(name);
10486 Roo.data.Record.AUTO_ID = 1000;
10487 Roo.data.Record.EDIT = 'edit';
10488 Roo.data.Record.REJECT = 'reject';
10489 Roo.data.Record.COMMIT = 'commit';
10491 Roo.data.Record.prototype = {
10493 * Readonly flag - true if this record has been modified.
10502 join : function(store){
10503 this.store = store;
10507 * Set the named field to the specified value.
10508 * @param {String} name The name of the field to set.
10509 * @param {Object} value The value to set the field to.
10511 set : function(name, value){
10512 if(this.data[name] == value){
10516 if(!this.modified){
10517 this.modified = {};
10519 if(typeof this.modified[name] == 'undefined'){
10520 this.modified[name] = this.data[name];
10522 this.data[name] = value;
10523 if(!this.editing && this.store){
10524 this.store.afterEdit(this);
10529 * Get the value of the named field.
10530 * @param {String} name The name of the field to get the value of.
10531 * @return {Object} The value of the field.
10533 get : function(name){
10534 return this.data[name];
10538 beginEdit : function(){
10539 this.editing = true;
10540 this.modified = {};
10544 cancelEdit : function(){
10545 this.editing = false;
10546 delete this.modified;
10550 endEdit : function(){
10551 this.editing = false;
10552 if(this.dirty && this.store){
10553 this.store.afterEdit(this);
10558 * Usually called by the {@link Roo.data.Store} which owns the Record.
10559 * Rejects all changes made to the Record since either creation, or the last commit operation.
10560 * Modified fields are reverted to their original values.
10562 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10563 * of reject operations.
10565 reject : function(){
10566 var m = this.modified;
10568 if(typeof m[n] != "function"){
10569 this.data[n] = m[n];
10572 this.dirty = false;
10573 delete this.modified;
10574 this.editing = false;
10576 this.store.afterReject(this);
10581 * Usually called by the {@link Roo.data.Store} which owns the Record.
10582 * Commits all changes made to the Record since either creation, or the last commit operation.
10584 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10585 * of commit operations.
10587 commit : function(){
10588 this.dirty = false;
10589 delete this.modified;
10590 this.editing = false;
10592 this.store.afterCommit(this);
10597 hasError : function(){
10598 return this.error != null;
10602 clearError : function(){
10607 * Creates a copy of this record.
10608 * @param {String} id (optional) A new record id if you don't want to use this record's id
10611 copy : function(newId) {
10612 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10616 * Ext JS Library 1.1.1
10617 * Copyright(c) 2006-2007, Ext JS, LLC.
10619 * Originally Released Under LGPL - original licence link has changed is not relivant.
10622 * <script type="text/javascript">
10628 * @class Roo.data.Store
10629 * @extends Roo.util.Observable
10630 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10631 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10633 * 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
10634 * has no knowledge of the format of the data returned by the Proxy.<br>
10636 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10637 * instances from the data object. These records are cached and made available through accessor functions.
10639 * Creates a new Store.
10640 * @param {Object} config A config object containing the objects needed for the Store to access data,
10641 * and read the data into Records.
10643 Roo.data.Store = function(config){
10644 this.data = new Roo.util.MixedCollection(false);
10645 this.data.getKey = function(o){
10648 this.baseParams = {};
10650 this.paramNames = {
10655 "multisort" : "_multisort"
10658 if(config && config.data){
10659 this.inlineData = config.data;
10660 delete config.data;
10663 Roo.apply(this, config);
10665 if(this.reader){ // reader passed
10666 this.reader = Roo.factory(this.reader, Roo.data);
10667 this.reader.xmodule = this.xmodule || false;
10668 if(!this.recordType){
10669 this.recordType = this.reader.recordType;
10671 if(this.reader.onMetaChange){
10672 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10676 if(this.recordType){
10677 this.fields = this.recordType.prototype.fields;
10679 this.modified = [];
10683 * @event datachanged
10684 * Fires when the data cache has changed, and a widget which is using this Store
10685 * as a Record cache should refresh its view.
10686 * @param {Store} this
10688 datachanged : true,
10690 * @event metachange
10691 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10692 * @param {Store} this
10693 * @param {Object} meta The JSON metadata
10698 * Fires when Records have been added to the Store
10699 * @param {Store} this
10700 * @param {Roo.data.Record[]} records The array of Records added
10701 * @param {Number} index The index at which the record(s) were added
10706 * Fires when a Record has been removed from the Store
10707 * @param {Store} this
10708 * @param {Roo.data.Record} record The Record that was removed
10709 * @param {Number} index The index at which the record was removed
10714 * Fires when a Record has been updated
10715 * @param {Store} this
10716 * @param {Roo.data.Record} record The Record that was updated
10717 * @param {String} operation The update operation being performed. Value may be one of:
10719 Roo.data.Record.EDIT
10720 Roo.data.Record.REJECT
10721 Roo.data.Record.COMMIT
10727 * Fires when the data cache has been cleared.
10728 * @param {Store} this
10732 * @event beforeload
10733 * Fires before a request is made for a new data object. If the beforeload handler returns false
10734 * the load action will be canceled.
10735 * @param {Store} this
10736 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10740 * @event beforeloadadd
10741 * Fires after a new set of Records has been loaded.
10742 * @param {Store} this
10743 * @param {Roo.data.Record[]} records The Records that were loaded
10744 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10746 beforeloadadd : true,
10749 * Fires after a new set of Records has been loaded, before they are added to the store.
10750 * @param {Store} this
10751 * @param {Roo.data.Record[]} records The Records that were loaded
10752 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10753 * @params {Object} return from reader
10757 * @event loadexception
10758 * Fires if an exception occurs in the Proxy during loading.
10759 * Called with the signature of the Proxy's "loadexception" event.
10760 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10763 * @param {Object} return from JsonData.reader() - success, totalRecords, records
10764 * @param {Object} load options
10765 * @param {Object} jsonData from your request (normally this contains the Exception)
10767 loadexception : true
10771 this.proxy = Roo.factory(this.proxy, Roo.data);
10772 this.proxy.xmodule = this.xmodule || false;
10773 this.relayEvents(this.proxy, ["loadexception"]);
10775 this.sortToggle = {};
10776 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10778 Roo.data.Store.superclass.constructor.call(this);
10780 if(this.inlineData){
10781 this.loadData(this.inlineData);
10782 delete this.inlineData;
10786 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10788 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
10789 * without a remote query - used by combo/forms at present.
10793 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10796 * @cfg {Array} data Inline data to be loaded when the store is initialized.
10799 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10800 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10803 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10804 * on any HTTP request
10807 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10810 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10814 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10815 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10817 remoteSort : false,
10820 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10821 * loaded or when a record is removed. (defaults to false).
10823 pruneModifiedRecords : false,
10826 lastOptions : null,
10829 * Add Records to the Store and fires the add event.
10830 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10832 add : function(records){
10833 records = [].concat(records);
10834 for(var i = 0, len = records.length; i < len; i++){
10835 records[i].join(this);
10837 var index = this.data.length;
10838 this.data.addAll(records);
10839 this.fireEvent("add", this, records, index);
10843 * Remove a Record from the Store and fires the remove event.
10844 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10846 remove : function(record){
10847 var index = this.data.indexOf(record);
10848 this.data.removeAt(index);
10849 if(this.pruneModifiedRecords){
10850 this.modified.remove(record);
10852 this.fireEvent("remove", this, record, index);
10856 * Remove all Records from the Store and fires the clear event.
10858 removeAll : function(){
10860 if(this.pruneModifiedRecords){
10861 this.modified = [];
10863 this.fireEvent("clear", this);
10867 * Inserts Records to the Store at the given index and fires the add event.
10868 * @param {Number} index The start index at which to insert the passed Records.
10869 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10871 insert : function(index, records){
10872 records = [].concat(records);
10873 for(var i = 0, len = records.length; i < len; i++){
10874 this.data.insert(index, records[i]);
10875 records[i].join(this);
10877 this.fireEvent("add", this, records, index);
10881 * Get the index within the cache of the passed Record.
10882 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10883 * @return {Number} The index of the passed Record. Returns -1 if not found.
10885 indexOf : function(record){
10886 return this.data.indexOf(record);
10890 * Get the index within the cache of the Record with the passed id.
10891 * @param {String} id The id of the Record to find.
10892 * @return {Number} The index of the Record. Returns -1 if not found.
10894 indexOfId : function(id){
10895 return this.data.indexOfKey(id);
10899 * Get the Record with the specified id.
10900 * @param {String} id The id of the Record to find.
10901 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10903 getById : function(id){
10904 return this.data.key(id);
10908 * Get the Record at the specified index.
10909 * @param {Number} index The index of the Record to find.
10910 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10912 getAt : function(index){
10913 return this.data.itemAt(index);
10917 * Returns a range of Records between specified indices.
10918 * @param {Number} startIndex (optional) The starting index (defaults to 0)
10919 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10920 * @return {Roo.data.Record[]} An array of Records
10922 getRange : function(start, end){
10923 return this.data.getRange(start, end);
10927 storeOptions : function(o){
10928 o = Roo.apply({}, o);
10931 this.lastOptions = o;
10935 * Loads the Record cache from the configured Proxy using the configured Reader.
10937 * If using remote paging, then the first load call must specify the <em>start</em>
10938 * and <em>limit</em> properties in the options.params property to establish the initial
10939 * position within the dataset, and the number of Records to cache on each read from the Proxy.
10941 * <strong>It is important to note that for remote data sources, loading is asynchronous,
10942 * and this call will return before the new data has been loaded. Perform any post-processing
10943 * in a callback function, or in a "load" event handler.</strong>
10945 * @param {Object} options An object containing properties which control loading options:<ul>
10946 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10947 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10948 * passed the following arguments:<ul>
10949 * <li>r : Roo.data.Record[]</li>
10950 * <li>options: Options object from the load call</li>
10951 * <li>success: Boolean success indicator</li></ul></li>
10952 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10953 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10956 load : function(options){
10957 options = options || {};
10958 if(this.fireEvent("beforeload", this, options) !== false){
10959 this.storeOptions(options);
10960 var p = Roo.apply(options.params || {}, this.baseParams);
10961 // if meta was not loaded from remote source.. try requesting it.
10962 if (!this.reader.metaFromRemote) {
10963 p._requestMeta = 1;
10965 if(this.sortInfo && this.remoteSort){
10966 var pn = this.paramNames;
10967 p[pn["sort"]] = this.sortInfo.field;
10968 p[pn["dir"]] = this.sortInfo.direction;
10970 if (this.multiSort) {
10971 var pn = this.paramNames;
10972 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10975 this.proxy.load(p, this.reader, this.loadRecords, this, options);
10980 * Reloads the Record cache from the configured Proxy using the configured Reader and
10981 * the options from the last load operation performed.
10982 * @param {Object} options (optional) An object containing properties which may override the options
10983 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10984 * the most recently used options are reused).
10986 reload : function(options){
10987 this.load(Roo.applyIf(options||{}, this.lastOptions));
10991 // Called as a callback by the Reader during a load operation.
10992 loadRecords : function(o, options, success){
10993 if(!o || success === false){
10994 if(success !== false){
10995 this.fireEvent("load", this, [], options, o);
10997 if(options.callback){
10998 options.callback.call(options.scope || this, [], options, false);
11002 // if data returned failure - throw an exception.
11003 if (o.success === false) {
11004 // show a message if no listener is registered.
11005 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11006 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11008 // loadmask wil be hooked into this..
11009 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11012 var r = o.records, t = o.totalRecords || r.length;
11014 this.fireEvent("beforeloadadd", this, r, options, o);
11016 if(!options || options.add !== true){
11017 if(this.pruneModifiedRecords){
11018 this.modified = [];
11020 for(var i = 0, len = r.length; i < len; i++){
11024 this.data = this.snapshot;
11025 delete this.snapshot;
11028 this.data.addAll(r);
11029 this.totalLength = t;
11031 this.fireEvent("datachanged", this);
11033 this.totalLength = Math.max(t, this.data.length+r.length);
11037 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11039 var e = new Roo.data.Record({});
11041 e.set(this.parent.displayField, this.parent.emptyTitle);
11042 e.set(this.parent.valueField, '');
11047 this.fireEvent("load", this, r, options, o);
11048 if(options.callback){
11049 options.callback.call(options.scope || this, r, options, true);
11055 * Loads data from a passed data block. A Reader which understands the format of the data
11056 * must have been configured in the constructor.
11057 * @param {Object} data The data block from which to read the Records. The format of the data expected
11058 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11059 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11061 loadData : function(o, append){
11062 var r = this.reader.readRecords(o);
11063 this.loadRecords(r, {add: append}, true);
11067 * Gets the number of cached records.
11069 * <em>If using paging, this may not be the total size of the dataset. If the data object
11070 * used by the Reader contains the dataset size, then the getTotalCount() function returns
11071 * the data set size</em>
11073 getCount : function(){
11074 return this.data.length || 0;
11078 * Gets the total number of records in the dataset as returned by the server.
11080 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11081 * the dataset size</em>
11083 getTotalCount : function(){
11084 return this.totalLength || 0;
11088 * Returns the sort state of the Store as an object with two properties:
11090 field {String} The name of the field by which the Records are sorted
11091 direction {String} The sort order, "ASC" or "DESC"
11094 getSortState : function(){
11095 return this.sortInfo;
11099 applySort : function(){
11100 if(this.sortInfo && !this.remoteSort){
11101 var s = this.sortInfo, f = s.field;
11102 var st = this.fields.get(f).sortType;
11103 var fn = function(r1, r2){
11104 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11105 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11107 this.data.sort(s.direction, fn);
11108 if(this.snapshot && this.snapshot != this.data){
11109 this.snapshot.sort(s.direction, fn);
11115 * Sets the default sort column and order to be used by the next load operation.
11116 * @param {String} fieldName The name of the field to sort by.
11117 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11119 setDefaultSort : function(field, dir){
11120 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11124 * Sort the Records.
11125 * If remote sorting is used, the sort is performed on the server, and the cache is
11126 * reloaded. If local sorting is used, the cache is sorted internally.
11127 * @param {String} fieldName The name of the field to sort by.
11128 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11130 sort : function(fieldName, dir){
11131 var f = this.fields.get(fieldName);
11133 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11135 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11136 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11141 this.sortToggle[f.name] = dir;
11142 this.sortInfo = {field: f.name, direction: dir};
11143 if(!this.remoteSort){
11145 this.fireEvent("datachanged", this);
11147 this.load(this.lastOptions);
11152 * Calls the specified function for each of the Records in the cache.
11153 * @param {Function} fn The function to call. The Record is passed as the first parameter.
11154 * Returning <em>false</em> aborts and exits the iteration.
11155 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11157 each : function(fn, scope){
11158 this.data.each(fn, scope);
11162 * Gets all records modified since the last commit. Modified records are persisted across load operations
11163 * (e.g., during paging).
11164 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11166 getModifiedRecords : function(){
11167 return this.modified;
11171 createFilterFn : function(property, value, anyMatch){
11172 if(!value.exec){ // not a regex
11173 value = String(value);
11174 if(value.length == 0){
11177 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11179 return function(r){
11180 return value.test(r.data[property]);
11185 * Sums the value of <i>property</i> for each record between start and end and returns the result.
11186 * @param {String} property A field on your records
11187 * @param {Number} start The record index to start at (defaults to 0)
11188 * @param {Number} end The last record index to include (defaults to length - 1)
11189 * @return {Number} The sum
11191 sum : function(property, start, end){
11192 var rs = this.data.items, v = 0;
11193 start = start || 0;
11194 end = (end || end === 0) ? end : rs.length-1;
11196 for(var i = start; i <= end; i++){
11197 v += (rs[i].data[property] || 0);
11203 * Filter the records by a specified property.
11204 * @param {String} field A field on your records
11205 * @param {String/RegExp} value Either a string that the field
11206 * should start with or a RegExp to test against the field
11207 * @param {Boolean} anyMatch True to match any part not just the beginning
11209 filter : function(property, value, anyMatch){
11210 var fn = this.createFilterFn(property, value, anyMatch);
11211 return fn ? this.filterBy(fn) : this.clearFilter();
11215 * Filter by a function. The specified function will be called with each
11216 * record in this data source. If the function returns true the record is included,
11217 * otherwise it is filtered.
11218 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11219 * @param {Object} scope (optional) The scope of the function (defaults to this)
11221 filterBy : function(fn, scope){
11222 this.snapshot = this.snapshot || this.data;
11223 this.data = this.queryBy(fn, scope||this);
11224 this.fireEvent("datachanged", this);
11228 * Query the records by a specified property.
11229 * @param {String} field A field on your records
11230 * @param {String/RegExp} value Either a string that the field
11231 * should start with or a RegExp to test against the field
11232 * @param {Boolean} anyMatch True to match any part not just the beginning
11233 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11235 query : function(property, value, anyMatch){
11236 var fn = this.createFilterFn(property, value, anyMatch);
11237 return fn ? this.queryBy(fn) : this.data.clone();
11241 * Query by a function. The specified function will be called with each
11242 * record in this data source. If the function returns true the record is included
11244 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11245 * @param {Object} scope (optional) The scope of the function (defaults to this)
11246 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11248 queryBy : function(fn, scope){
11249 var data = this.snapshot || this.data;
11250 return data.filterBy(fn, scope||this);
11254 * Collects unique values for a particular dataIndex from this store.
11255 * @param {String} dataIndex The property to collect
11256 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11257 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11258 * @return {Array} An array of the unique values
11260 collect : function(dataIndex, allowNull, bypassFilter){
11261 var d = (bypassFilter === true && this.snapshot) ?
11262 this.snapshot.items : this.data.items;
11263 var v, sv, r = [], l = {};
11264 for(var i = 0, len = d.length; i < len; i++){
11265 v = d[i].data[dataIndex];
11267 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11276 * Revert to a view of the Record cache with no filtering applied.
11277 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11279 clearFilter : function(suppressEvent){
11280 if(this.snapshot && this.snapshot != this.data){
11281 this.data = this.snapshot;
11282 delete this.snapshot;
11283 if(suppressEvent !== true){
11284 this.fireEvent("datachanged", this);
11290 afterEdit : function(record){
11291 if(this.modified.indexOf(record) == -1){
11292 this.modified.push(record);
11294 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11298 afterReject : function(record){
11299 this.modified.remove(record);
11300 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11304 afterCommit : function(record){
11305 this.modified.remove(record);
11306 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11310 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11311 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11313 commitChanges : function(){
11314 var m = this.modified.slice(0);
11315 this.modified = [];
11316 for(var i = 0, len = m.length; i < len; i++){
11322 * Cancel outstanding changes on all changed records.
11324 rejectChanges : function(){
11325 var m = this.modified.slice(0);
11326 this.modified = [];
11327 for(var i = 0, len = m.length; i < len; i++){
11332 onMetaChange : function(meta, rtype, o){
11333 this.recordType = rtype;
11334 this.fields = rtype.prototype.fields;
11335 delete this.snapshot;
11336 this.sortInfo = meta.sortInfo || this.sortInfo;
11337 this.modified = [];
11338 this.fireEvent('metachange', this, this.reader.meta);
11341 moveIndex : function(data, type)
11343 var index = this.indexOf(data);
11345 var newIndex = index + type;
11349 this.insert(newIndex, data);
11354 * Ext JS Library 1.1.1
11355 * Copyright(c) 2006-2007, Ext JS, LLC.
11357 * Originally Released Under LGPL - original licence link has changed is not relivant.
11360 * <script type="text/javascript">
11364 * @class Roo.data.SimpleStore
11365 * @extends Roo.data.Store
11366 * Small helper class to make creating Stores from Array data easier.
11367 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11368 * @cfg {Array} fields An array of field definition objects, or field name strings.
11369 * @cfg {Array} data The multi-dimensional array of data
11371 * @param {Object} config
11373 Roo.data.SimpleStore = function(config){
11374 Roo.data.SimpleStore.superclass.constructor.call(this, {
11376 reader: new Roo.data.ArrayReader({
11379 Roo.data.Record.create(config.fields)
11381 proxy : new Roo.data.MemoryProxy(config.data)
11385 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11387 * Ext JS Library 1.1.1
11388 * Copyright(c) 2006-2007, Ext JS, LLC.
11390 * Originally Released Under LGPL - original licence link has changed is not relivant.
11393 * <script type="text/javascript">
11398 * @extends Roo.data.Store
11399 * @class Roo.data.JsonStore
11400 * Small helper class to make creating Stores for JSON data easier. <br/>
11402 var store = new Roo.data.JsonStore({
11403 url: 'get-images.php',
11405 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11408 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11409 * JsonReader and HttpProxy (unless inline data is provided).</b>
11410 * @cfg {Array} fields An array of field definition objects, or field name strings.
11412 * @param {Object} config
11414 Roo.data.JsonStore = function(c){
11415 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11416 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11417 reader: new Roo.data.JsonReader(c, c.fields)
11420 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11422 * Ext JS Library 1.1.1
11423 * Copyright(c) 2006-2007, Ext JS, LLC.
11425 * Originally Released Under LGPL - original licence link has changed is not relivant.
11428 * <script type="text/javascript">
11432 Roo.data.Field = function(config){
11433 if(typeof config == "string"){
11434 config = {name: config};
11436 Roo.apply(this, config);
11439 this.type = "auto";
11442 var st = Roo.data.SortTypes;
11443 // named sortTypes are supported, here we look them up
11444 if(typeof this.sortType == "string"){
11445 this.sortType = st[this.sortType];
11448 // set default sortType for strings and dates
11449 if(!this.sortType){
11452 this.sortType = st.asUCString;
11455 this.sortType = st.asDate;
11458 this.sortType = st.none;
11463 var stripRe = /[\$,%]/g;
11465 // prebuilt conversion function for this field, instead of
11466 // switching every time we're reading a value
11468 var cv, dateFormat = this.dateFormat;
11473 cv = function(v){ return v; };
11476 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11480 return v !== undefined && v !== null && v !== '' ?
11481 parseInt(String(v).replace(stripRe, ""), 10) : '';
11486 return v !== undefined && v !== null && v !== '' ?
11487 parseFloat(String(v).replace(stripRe, ""), 10) : '';
11492 cv = function(v){ return v === true || v === "true" || v == 1; };
11499 if(v instanceof Date){
11503 if(dateFormat == "timestamp"){
11504 return new Date(v*1000);
11506 return Date.parseDate(v, dateFormat);
11508 var parsed = Date.parse(v);
11509 return parsed ? new Date(parsed) : null;
11518 Roo.data.Field.prototype = {
11526 * Ext JS Library 1.1.1
11527 * Copyright(c) 2006-2007, Ext JS, LLC.
11529 * Originally Released Under LGPL - original licence link has changed is not relivant.
11532 * <script type="text/javascript">
11535 // Base class for reading structured data from a data source. This class is intended to be
11536 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11539 * @class Roo.data.DataReader
11540 * Base class for reading structured data from a data source. This class is intended to be
11541 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11544 Roo.data.DataReader = function(meta, recordType){
11548 this.recordType = recordType instanceof Array ?
11549 Roo.data.Record.create(recordType) : recordType;
11552 Roo.data.DataReader.prototype = {
11554 * Create an empty record
11555 * @param {Object} data (optional) - overlay some values
11556 * @return {Roo.data.Record} record created.
11558 newRow : function(d) {
11560 this.recordType.prototype.fields.each(function(c) {
11562 case 'int' : da[c.name] = 0; break;
11563 case 'date' : da[c.name] = new Date(); break;
11564 case 'float' : da[c.name] = 0.0; break;
11565 case 'boolean' : da[c.name] = false; break;
11566 default : da[c.name] = ""; break;
11570 return new this.recordType(Roo.apply(da, d));
11575 * Ext JS Library 1.1.1
11576 * Copyright(c) 2006-2007, Ext JS, LLC.
11578 * Originally Released Under LGPL - original licence link has changed is not relivant.
11581 * <script type="text/javascript">
11585 * @class Roo.data.DataProxy
11586 * @extends Roo.data.Observable
11587 * This class is an abstract base class for implementations which provide retrieval of
11588 * unformatted data objects.<br>
11590 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11591 * (of the appropriate type which knows how to parse the data object) to provide a block of
11592 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11594 * Custom implementations must implement the load method as described in
11595 * {@link Roo.data.HttpProxy#load}.
11597 Roo.data.DataProxy = function(){
11600 * @event beforeload
11601 * Fires before a network request is made to retrieve a data object.
11602 * @param {Object} This DataProxy object.
11603 * @param {Object} params The params parameter to the load function.
11608 * Fires before the load method's callback is called.
11609 * @param {Object} This DataProxy object.
11610 * @param {Object} o The data object.
11611 * @param {Object} arg The callback argument object passed to the load function.
11615 * @event loadexception
11616 * Fires if an Exception occurs during data retrieval.
11617 * @param {Object} This DataProxy object.
11618 * @param {Object} o The data object.
11619 * @param {Object} arg The callback argument object passed to the load function.
11620 * @param {Object} e The Exception.
11622 loadexception : true
11624 Roo.data.DataProxy.superclass.constructor.call(this);
11627 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11630 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11634 * Ext JS Library 1.1.1
11635 * Copyright(c) 2006-2007, Ext JS, LLC.
11637 * Originally Released Under LGPL - original licence link has changed is not relivant.
11640 * <script type="text/javascript">
11643 * @class Roo.data.MemoryProxy
11644 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11645 * to the Reader when its load method is called.
11647 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11649 Roo.data.MemoryProxy = function(data){
11653 Roo.data.MemoryProxy.superclass.constructor.call(this);
11657 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11660 * Load data from the requested source (in this case an in-memory
11661 * data object passed to the constructor), read the data object into
11662 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11663 * process that block using the passed callback.
11664 * @param {Object} params This parameter is not used by the MemoryProxy class.
11665 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11666 * object into a block of Roo.data.Records.
11667 * @param {Function} callback The function into which to pass the block of Roo.data.records.
11668 * The function must be passed <ul>
11669 * <li>The Record block object</li>
11670 * <li>The "arg" argument from the load function</li>
11671 * <li>A boolean success indicator</li>
11673 * @param {Object} scope The scope in which to call the callback
11674 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11676 load : function(params, reader, callback, scope, arg){
11677 params = params || {};
11680 result = reader.readRecords(this.data);
11682 this.fireEvent("loadexception", this, arg, null, e);
11683 callback.call(scope, null, arg, false);
11686 callback.call(scope, result, arg, true);
11690 update : function(params, records){
11695 * Ext JS Library 1.1.1
11696 * Copyright(c) 2006-2007, Ext JS, LLC.
11698 * Originally Released Under LGPL - original licence link has changed is not relivant.
11701 * <script type="text/javascript">
11704 * @class Roo.data.HttpProxy
11705 * @extends Roo.data.DataProxy
11706 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11707 * configured to reference a certain URL.<br><br>
11709 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11710 * from which the running page was served.<br><br>
11712 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11714 * Be aware that to enable the browser to parse an XML document, the server must set
11715 * the Content-Type header in the HTTP response to "text/xml".
11717 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11718 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
11719 * will be used to make the request.
11721 Roo.data.HttpProxy = function(conn){
11722 Roo.data.HttpProxy.superclass.constructor.call(this);
11723 // is conn a conn config or a real conn?
11725 this.useAjax = !conn || !conn.events;
11729 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11730 // thse are take from connection...
11733 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11736 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11737 * extra parameters to each request made by this object. (defaults to undefined)
11740 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11741 * to each request made by this object. (defaults to undefined)
11744 * @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)
11747 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11750 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11756 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11760 * Return the {@link Roo.data.Connection} object being used by this Proxy.
11761 * @return {Connection} The Connection object. This object may be used to subscribe to events on
11762 * a finer-grained basis than the DataProxy events.
11764 getConnection : function(){
11765 return this.useAjax ? Roo.Ajax : this.conn;
11769 * Load data from the configured {@link Roo.data.Connection}, read the data object into
11770 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11771 * process that block using the passed callback.
11772 * @param {Object} params An object containing properties which are to be used as HTTP parameters
11773 * for the request to the remote server.
11774 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11775 * object into a block of Roo.data.Records.
11776 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11777 * The function must be passed <ul>
11778 * <li>The Record block object</li>
11779 * <li>The "arg" argument from the load function</li>
11780 * <li>A boolean success indicator</li>
11782 * @param {Object} scope The scope in which to call the callback
11783 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11785 load : function(params, reader, callback, scope, arg){
11786 if(this.fireEvent("beforeload", this, params) !== false){
11788 params : params || {},
11790 callback : callback,
11795 callback : this.loadResponse,
11799 Roo.applyIf(o, this.conn);
11800 if(this.activeRequest){
11801 Roo.Ajax.abort(this.activeRequest);
11803 this.activeRequest = Roo.Ajax.request(o);
11805 this.conn.request(o);
11808 callback.call(scope||this, null, arg, false);
11813 loadResponse : function(o, success, response){
11814 delete this.activeRequest;
11816 this.fireEvent("loadexception", this, o, response);
11817 o.request.callback.call(o.request.scope, null, o.request.arg, false);
11822 result = o.reader.read(response);
11824 this.fireEvent("loadexception", this, o, response, e);
11825 o.request.callback.call(o.request.scope, null, o.request.arg, false);
11829 this.fireEvent("load", this, o, o.request.arg);
11830 o.request.callback.call(o.request.scope, result, o.request.arg, true);
11834 update : function(dataSet){
11839 updateResponse : function(dataSet){
11844 * Ext JS Library 1.1.1
11845 * Copyright(c) 2006-2007, Ext JS, LLC.
11847 * Originally Released Under LGPL - original licence link has changed is not relivant.
11850 * <script type="text/javascript">
11854 * @class Roo.data.ScriptTagProxy
11855 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11856 * other than the originating domain of the running page.<br><br>
11858 * <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
11859 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11861 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11862 * source code that is used as the source inside a <script> tag.<br><br>
11864 * In order for the browser to process the returned data, the server must wrap the data object
11865 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11866 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11867 * depending on whether the callback name was passed:
11870 boolean scriptTag = false;
11871 String cb = request.getParameter("callback");
11874 response.setContentType("text/javascript");
11876 response.setContentType("application/x-json");
11878 Writer out = response.getWriter();
11880 out.write(cb + "(");
11882 out.print(dataBlock.toJsonString());
11889 * @param {Object} config A configuration object.
11891 Roo.data.ScriptTagProxy = function(config){
11892 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11893 Roo.apply(this, config);
11894 this.head = document.getElementsByTagName("head")[0];
11897 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11899 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11901 * @cfg {String} url The URL from which to request the data object.
11904 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11908 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11909 * the server the name of the callback function set up by the load call to process the returned data object.
11910 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11911 * javascript output which calls this named function passing the data object as its only parameter.
11913 callbackParam : "callback",
11915 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11916 * name to the request.
11921 * Load data from the configured URL, read the data object into
11922 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11923 * process that block using the passed callback.
11924 * @param {Object} params An object containing properties which are to be used as HTTP parameters
11925 * for the request to the remote server.
11926 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11927 * object into a block of Roo.data.Records.
11928 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11929 * The function must be passed <ul>
11930 * <li>The Record block object</li>
11931 * <li>The "arg" argument from the load function</li>
11932 * <li>A boolean success indicator</li>
11934 * @param {Object} scope The scope in which to call the callback
11935 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11937 load : function(params, reader, callback, scope, arg){
11938 if(this.fireEvent("beforeload", this, params) !== false){
11940 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11942 var url = this.url;
11943 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11945 url += "&_dc=" + (new Date().getTime());
11947 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11950 cb : "stcCallback"+transId,
11951 scriptId : "stcScript"+transId,
11955 callback : callback,
11961 window[trans.cb] = function(o){
11962 conn.handleResponse(o, trans);
11965 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11967 if(this.autoAbort !== false){
11971 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11973 var script = document.createElement("script");
11974 script.setAttribute("src", url);
11975 script.setAttribute("type", "text/javascript");
11976 script.setAttribute("id", trans.scriptId);
11977 this.head.appendChild(script);
11979 this.trans = trans;
11981 callback.call(scope||this, null, arg, false);
11986 isLoading : function(){
11987 return this.trans ? true : false;
11991 * Abort the current server request.
11993 abort : function(){
11994 if(this.isLoading()){
11995 this.destroyTrans(this.trans);
12000 destroyTrans : function(trans, isLoaded){
12001 this.head.removeChild(document.getElementById(trans.scriptId));
12002 clearTimeout(trans.timeoutId);
12004 window[trans.cb] = undefined;
12006 delete window[trans.cb];
12009 // if hasn't been loaded, wait for load to remove it to prevent script error
12010 window[trans.cb] = function(){
12011 window[trans.cb] = undefined;
12013 delete window[trans.cb];
12020 handleResponse : function(o, trans){
12021 this.trans = false;
12022 this.destroyTrans(trans, true);
12025 result = trans.reader.readRecords(o);
12027 this.fireEvent("loadexception", this, o, trans.arg, e);
12028 trans.callback.call(trans.scope||window, null, trans.arg, false);
12031 this.fireEvent("load", this, o, trans.arg);
12032 trans.callback.call(trans.scope||window, result, trans.arg, true);
12036 handleFailure : function(trans){
12037 this.trans = false;
12038 this.destroyTrans(trans, false);
12039 this.fireEvent("loadexception", this, null, trans.arg);
12040 trans.callback.call(trans.scope||window, null, trans.arg, false);
12044 * Ext JS Library 1.1.1
12045 * Copyright(c) 2006-2007, Ext JS, LLC.
12047 * Originally Released Under LGPL - original licence link has changed is not relivant.
12050 * <script type="text/javascript">
12054 * @class Roo.data.JsonReader
12055 * @extends Roo.data.DataReader
12056 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12057 * based on mappings in a provided Roo.data.Record constructor.
12059 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12060 * in the reply previously.
12065 var RecordDef = Roo.data.Record.create([
12066 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
12067 {name: 'occupation'} // This field will use "occupation" as the mapping.
12069 var myReader = new Roo.data.JsonReader({
12070 totalProperty: "results", // The property which contains the total dataset size (optional)
12071 root: "rows", // The property which contains an Array of row objects
12072 id: "id" // The property within each row object that provides an ID for the record (optional)
12076 * This would consume a JSON file like this:
12078 { 'results': 2, 'rows': [
12079 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12080 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12083 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12084 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12085 * paged from the remote server.
12086 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12087 * @cfg {String} root name of the property which contains the Array of row objects.
12088 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12089 * @cfg {Array} fields Array of field definition objects
12091 * Create a new JsonReader
12092 * @param {Object} meta Metadata configuration options
12093 * @param {Object} recordType Either an Array of field definition objects,
12094 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12096 Roo.data.JsonReader = function(meta, recordType){
12099 // set some defaults:
12100 Roo.applyIf(meta, {
12101 totalProperty: 'total',
12102 successProperty : 'success',
12107 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12109 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12112 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
12113 * Used by Store query builder to append _requestMeta to params.
12116 metaFromRemote : false,
12118 * This method is only used by a DataProxy which has retrieved data from a remote server.
12119 * @param {Object} response The XHR object which contains the JSON data in its responseText.
12120 * @return {Object} data A data block which is used by an Roo.data.Store object as
12121 * a cache of Roo.data.Records.
12123 read : function(response){
12124 var json = response.responseText;
12126 var o = /* eval:var:o */ eval("("+json+")");
12128 throw {message: "JsonReader.read: Json object not found"};
12134 this.metaFromRemote = true;
12135 this.meta = o.metaData;
12136 this.recordType = Roo.data.Record.create(o.metaData.fields);
12137 this.onMetaChange(this.meta, this.recordType, o);
12139 return this.readRecords(o);
12142 // private function a store will implement
12143 onMetaChange : function(meta, recordType, o){
12150 simpleAccess: function(obj, subsc) {
12157 getJsonAccessor: function(){
12159 return function(expr) {
12161 return(re.test(expr))
12162 ? new Function("obj", "return obj." + expr)
12167 return Roo.emptyFn;
12172 * Create a data block containing Roo.data.Records from an XML document.
12173 * @param {Object} o An object which contains an Array of row objects in the property specified
12174 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12175 * which contains the total size of the dataset.
12176 * @return {Object} data A data block which is used by an Roo.data.Store object as
12177 * a cache of Roo.data.Records.
12179 readRecords : function(o){
12181 * After any data loads, the raw JSON data is available for further custom processing.
12185 var s = this.meta, Record = this.recordType,
12186 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12188 // Generate extraction functions for the totalProperty, the root, the id, and for each field
12190 if(s.totalProperty) {
12191 this.getTotal = this.getJsonAccessor(s.totalProperty);
12193 if(s.successProperty) {
12194 this.getSuccess = this.getJsonAccessor(s.successProperty);
12196 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12198 var g = this.getJsonAccessor(s.id);
12199 this.getId = function(rec) {
12201 return (r === undefined || r === "") ? null : r;
12204 this.getId = function(){return null;};
12207 for(var jj = 0; jj < fl; jj++){
12209 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12210 this.ef[jj] = this.getJsonAccessor(map);
12214 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12215 if(s.totalProperty){
12216 var vt = parseInt(this.getTotal(o), 10);
12221 if(s.successProperty){
12222 var vs = this.getSuccess(o);
12223 if(vs === false || vs === 'false'){
12228 for(var i = 0; i < c; i++){
12231 var id = this.getId(n);
12232 for(var j = 0; j < fl; j++){
12234 var v = this.ef[j](n);
12236 Roo.log('missing convert for ' + f.name);
12240 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12242 var record = new Record(values, id);
12244 records[i] = record;
12250 totalRecords : totalRecords
12255 * Ext JS Library 1.1.1
12256 * Copyright(c) 2006-2007, Ext JS, LLC.
12258 * Originally Released Under LGPL - original licence link has changed is not relivant.
12261 * <script type="text/javascript">
12265 * @class Roo.data.ArrayReader
12266 * @extends Roo.data.DataReader
12267 * Data reader class to create an Array of Roo.data.Record objects from an Array.
12268 * Each element of that Array represents a row of data fields. The
12269 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12270 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12274 var RecordDef = Roo.data.Record.create([
12275 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
12276 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
12278 var myReader = new Roo.data.ArrayReader({
12279 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
12283 * This would consume an Array like this:
12285 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12287 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12289 * Create a new JsonReader
12290 * @param {Object} meta Metadata configuration options.
12291 * @param {Object} recordType Either an Array of field definition objects
12292 * as specified to {@link Roo.data.Record#create},
12293 * or an {@link Roo.data.Record} object
12294 * created using {@link Roo.data.Record#create}.
12296 Roo.data.ArrayReader = function(meta, recordType){
12297 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12300 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12302 * Create a data block containing Roo.data.Records from an XML document.
12303 * @param {Object} o An Array of row objects which represents the dataset.
12304 * @return {Object} data A data block which is used by an Roo.data.Store object as
12305 * a cache of Roo.data.Records.
12307 readRecords : function(o){
12308 var sid = this.meta ? this.meta.id : null;
12309 var recordType = this.recordType, fields = recordType.prototype.fields;
12312 for(var i = 0; i < root.length; i++){
12315 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12316 for(var j = 0, jlen = fields.length; j < jlen; j++){
12317 var f = fields.items[j];
12318 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12319 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12321 values[f.name] = v;
12323 var record = new recordType(values, id);
12325 records[records.length] = record;
12329 totalRecords : records.length
12338 * @class Roo.bootstrap.ComboBox
12339 * @extends Roo.bootstrap.TriggerField
12340 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12341 * @cfg {Boolean} append (true|false) default false
12342 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12343 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12344 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12345 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12346 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12347 * @cfg {Boolean} animate default true
12348 * @cfg {Boolean} emptyResultText only for touch device
12349 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12350 * @cfg {String} emptyTitle default ''
12352 * Create a new ComboBox.
12353 * @param {Object} config Configuration options
12355 Roo.bootstrap.ComboBox = function(config){
12356 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12360 * Fires when the dropdown list is expanded
12361 * @param {Roo.bootstrap.ComboBox} combo This combo box
12366 * Fires when the dropdown list is collapsed
12367 * @param {Roo.bootstrap.ComboBox} combo This combo box
12371 * @event beforeselect
12372 * Fires before a list item is selected. Return false to cancel the selection.
12373 * @param {Roo.bootstrap.ComboBox} combo This combo box
12374 * @param {Roo.data.Record} record The data record returned from the underlying store
12375 * @param {Number} index The index of the selected item in the dropdown list
12377 'beforeselect' : true,
12380 * Fires when a list item is selected
12381 * @param {Roo.bootstrap.ComboBox} combo This combo box
12382 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12383 * @param {Number} index The index of the selected item in the dropdown list
12387 * @event beforequery
12388 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12389 * The event object passed has these properties:
12390 * @param {Roo.bootstrap.ComboBox} combo This combo box
12391 * @param {String} query The query
12392 * @param {Boolean} forceAll true to force "all" query
12393 * @param {Boolean} cancel true to cancel the query
12394 * @param {Object} e The query event object
12396 'beforequery': true,
12399 * Fires when the 'add' icon is pressed (add a listener to enable add button)
12400 * @param {Roo.bootstrap.ComboBox} combo This combo box
12405 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12406 * @param {Roo.bootstrap.ComboBox} combo This combo box
12407 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12412 * Fires when the remove value from the combobox array
12413 * @param {Roo.bootstrap.ComboBox} combo This combo box
12417 * @event afterremove
12418 * Fires when the remove value from the combobox array
12419 * @param {Roo.bootstrap.ComboBox} combo This combo box
12421 'afterremove' : true,
12423 * @event specialfilter
12424 * Fires when specialfilter
12425 * @param {Roo.bootstrap.ComboBox} combo This combo box
12427 'specialfilter' : true,
12430 * Fires when tick the element
12431 * @param {Roo.bootstrap.ComboBox} combo This combo box
12435 * @event touchviewdisplay
12436 * Fires when touch view require special display (default is using displayField)
12437 * @param {Roo.bootstrap.ComboBox} combo This combo box
12438 * @param {Object} cfg set html .
12440 'touchviewdisplay' : true
12445 this.tickItems = [];
12447 this.selectedIndex = -1;
12448 if(this.mode == 'local'){
12449 if(config.queryDelay === undefined){
12450 this.queryDelay = 10;
12452 if(config.minChars === undefined){
12458 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12461 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12462 * rendering into an Roo.Editor, defaults to false)
12465 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12466 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12469 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12472 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12473 * the dropdown list (defaults to undefined, with no header element)
12477 * @cfg {String/Roo.Template} tpl The template to use to render the output
12481 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12483 listWidth: undefined,
12485 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12486 * mode = 'remote' or 'text' if mode = 'local')
12488 displayField: undefined,
12491 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12492 * mode = 'remote' or 'value' if mode = 'local').
12493 * Note: use of a valueField requires the user make a selection
12494 * in order for a value to be mapped.
12496 valueField: undefined,
12498 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12503 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12504 * field's data value (defaults to the underlying DOM element's name)
12506 hiddenName: undefined,
12508 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12512 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12514 selectedClass: 'active',
12517 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12521 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12522 * anchor positions (defaults to 'tl-bl')
12524 listAlign: 'tl-bl?',
12526 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12530 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
12531 * query specified by the allQuery config option (defaults to 'query')
12533 triggerAction: 'query',
12535 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12536 * (defaults to 4, does not apply if editable = false)
12540 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12541 * delay (typeAheadDelay) if it matches a known value (defaults to false)
12545 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12546 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12550 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12551 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
12555 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
12556 * when editable = true (defaults to false)
12558 selectOnFocus:false,
12560 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12562 queryParam: 'query',
12564 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
12565 * when mode = 'remote' (defaults to 'Loading...')
12567 loadingText: 'Loading...',
12569 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12573 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12577 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12578 * traditional select (defaults to true)
12582 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12586 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12590 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12591 * listWidth has a higher value)
12595 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12596 * allow the user to set arbitrary text into the field (defaults to false)
12598 forceSelection:false,
12600 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12601 * if typeAhead = true (defaults to 250)
12603 typeAheadDelay : 250,
12605 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12606 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12608 valueNotFoundText : undefined,
12610 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12612 blockFocus : false,
12615 * @cfg {Boolean} disableClear Disable showing of clear button.
12617 disableClear : false,
12619 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
12621 alwaysQuery : false,
12624 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
12629 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12631 invalidClass : "has-warning",
12634 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12636 validClass : "has-success",
12639 * @cfg {Boolean} specialFilter (true|false) special filter default false
12641 specialFilter : false,
12644 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12646 mobileTouchView : true,
12649 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12651 useNativeIOS : false,
12653 ios_options : false,
12665 btnPosition : 'right',
12666 triggerList : true,
12667 showToggleBtn : true,
12669 emptyResultText: 'Empty',
12670 triggerText : 'Select',
12673 // element that contains real text value.. (when hidden is used..)
12675 getAutoCreate : function()
12680 * Render classic select for iso
12683 if(Roo.isIOS && this.useNativeIOS){
12684 cfg = this.getAutoCreateNativeIOS();
12692 if(Roo.isTouch && this.mobileTouchView){
12693 cfg = this.getAutoCreateTouchView();
12700 if(!this.tickable){
12701 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12702 if(this.name == 'info_year_invest_id_display_name'){
12703 Roo.log('cfg.................................................');
12710 * ComboBox with tickable selections
12713 var align = this.labelAlign || this.parentLabelAlign();
12716 cls : 'form-group roo-combobox-tickable' //input-group
12719 var btn_text_select = '';
12720 var btn_text_done = '';
12721 var btn_text_cancel = '';
12723 if (this.btn_text_show) {
12724 btn_text_select = 'Select';
12725 btn_text_done = 'Done';
12726 btn_text_cancel = 'Cancel';
12731 cls : 'tickable-buttons',
12736 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12737 //html : this.triggerText
12738 html: btn_text_select
12744 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12746 html: btn_text_done
12752 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12754 html: btn_text_cancel
12760 buttons.cn.unshift({
12762 cls: 'roo-select2-search-field-input'
12768 Roo.each(buttons.cn, function(c){
12770 c.cls += ' btn-' + _this.size;
12773 if (_this.disabled) {
12784 cls: 'form-hidden-field'
12788 cls: 'roo-select2-choices',
12792 cls: 'roo-select2-search-field',
12803 cls: 'roo-select2-container input-group roo-select2-container-multi',
12808 // cls: 'typeahead typeahead-long dropdown-menu',
12809 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
12814 if(this.hasFeedback && !this.allowBlank){
12818 cls: 'glyphicon form-control-feedback'
12821 combobox.cn.push(feedback);
12825 if (align ==='left' && this.fieldLabel.length) {
12827 cfg.cls += ' roo-form-group-label-left';
12832 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12833 tooltip : 'This field is required'
12838 cls : 'control-label',
12839 html : this.fieldLabel
12851 var labelCfg = cfg.cn[1];
12852 var contentCfg = cfg.cn[2];
12855 if(this.indicatorpos == 'right'){
12861 cls : 'control-label',
12865 html : this.fieldLabel
12869 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12870 tooltip : 'This field is required'
12885 labelCfg = cfg.cn[0];
12886 contentCfg = cfg.cn[1];
12890 if(this.labelWidth > 12){
12891 labelCfg.style = "width: " + this.labelWidth + 'px';
12894 if(this.labelWidth < 13 && this.labelmd == 0){
12895 this.labelmd = this.labelWidth;
12898 if(this.labellg > 0){
12899 labelCfg.cls += ' col-lg-' + this.labellg;
12900 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12903 if(this.labelmd > 0){
12904 labelCfg.cls += ' col-md-' + this.labelmd;
12905 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12908 if(this.labelsm > 0){
12909 labelCfg.cls += ' col-sm-' + this.labelsm;
12910 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12913 if(this.labelxs > 0){
12914 labelCfg.cls += ' col-xs-' + this.labelxs;
12915 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12919 } else if ( this.fieldLabel.length) {
12920 // Roo.log(" label");
12924 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12925 tooltip : 'This field is required'
12929 //cls : 'input-group-addon',
12930 html : this.fieldLabel
12935 if(this.indicatorpos == 'right'){
12936 Roo.log('hidden name:'+this.hiddenName);
12940 //cls : 'input-group-addon',
12941 html : this.fieldLabel
12945 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12946 tooltip : 'This field is required'
12955 // Roo.log(" no label && no align");
12962 ['xs','sm','md','lg'].map(function(size){
12963 if (settings[size]) {
12964 cfg.cls += ' col-' + size + '-' + settings[size];
12972 _initEventsCalled : false,
12975 initEvents: function()
12977 if (this._initEventsCalled) { // as we call render... prevent looping...
12980 this._initEventsCalled = true;
12983 throw "can not find store for combo";
12986 this.store = Roo.factory(this.store, Roo.data);
12987 this.store.parent = this;
12989 // if we are building from html. then this element is so complex, that we can not really
12990 // use the rendered HTML.
12991 // so we have to trash and replace the previous code.
12992 if (Roo.XComponent.build_from_html) {
12994 // remove this element....
12995 var e = this.el.dom, k=0;
12996 while (e ) { e = e.previousSibling; ++k;}
13001 this.rendered = false;
13003 this.render(this.parent().getChildContainer(true), k);
13009 if(Roo.isIOS && this.useNativeIOS){
13010 this.initIOSView();
13018 if(Roo.isTouch && this.mobileTouchView){
13019 this.initTouchView();
13024 this.initTickableEvents();
13028 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13030 if(this.hiddenName){
13032 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13034 this.hiddenField.dom.value =
13035 this.hiddenValue !== undefined ? this.hiddenValue :
13036 this.value !== undefined ? this.value : '';
13038 // prevent input submission
13039 this.el.dom.removeAttribute('name');
13040 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13045 // this.el.dom.setAttribute('autocomplete', 'off');
13048 var cls = 'x-combo-list';
13050 //this.list = new Roo.Layer({
13051 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13057 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13058 _this.list.setWidth(lw);
13061 this.list.on('mouseover', this.onViewOver, this);
13062 this.list.on('mousemove', this.onViewMove, this);
13064 this.list.on('scroll', this.onViewScroll, this);
13067 this.list.swallowEvent('mousewheel');
13068 this.assetHeight = 0;
13071 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13072 this.assetHeight += this.header.getHeight();
13075 this.innerList = this.list.createChild({cls:cls+'-inner'});
13076 this.innerList.on('mouseover', this.onViewOver, this);
13077 this.innerList.on('mousemove', this.onViewMove, this);
13078 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13080 if(this.allowBlank && !this.pageSize && !this.disableClear){
13081 this.footer = this.list.createChild({cls:cls+'-ft'});
13082 this.pageTb = new Roo.Toolbar(this.footer);
13086 this.footer = this.list.createChild({cls:cls+'-ft'});
13087 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13088 {pageSize: this.pageSize});
13092 if (this.pageTb && this.allowBlank && !this.disableClear) {
13094 this.pageTb.add(new Roo.Toolbar.Fill(), {
13095 cls: 'x-btn-icon x-btn-clear',
13097 handler: function()
13100 _this.clearValue();
13101 _this.onSelect(false, -1);
13106 this.assetHeight += this.footer.getHeight();
13111 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13114 this.view = new Roo.View(this.list, this.tpl, {
13115 singleSelect:true, store: this.store, selectedClass: this.selectedClass
13117 //this.view.wrapEl.setDisplayed(false);
13118 this.view.on('click', this.onViewClick, this);
13121 this.store.on('beforeload', this.onBeforeLoad, this);
13122 this.store.on('load', this.onLoad, this);
13123 this.store.on('loadexception', this.onLoadException, this);
13125 if(this.resizable){
13126 this.resizer = new Roo.Resizable(this.list, {
13127 pinned:true, handles:'se'
13129 this.resizer.on('resize', function(r, w, h){
13130 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13131 this.listWidth = w;
13132 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13133 this.restrictHeight();
13135 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13138 if(!this.editable){
13139 this.editable = true;
13140 this.setEditable(false);
13145 if (typeof(this.events.add.listeners) != 'undefined') {
13147 this.addicon = this.wrap.createChild(
13148 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
13150 this.addicon.on('click', function(e) {
13151 this.fireEvent('add', this);
13154 if (typeof(this.events.edit.listeners) != 'undefined') {
13156 this.editicon = this.wrap.createChild(
13157 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
13158 if (this.addicon) {
13159 this.editicon.setStyle('margin-left', '40px');
13161 this.editicon.on('click', function(e) {
13163 // we fire even if inothing is selected..
13164 this.fireEvent('edit', this, this.lastData );
13170 this.keyNav = new Roo.KeyNav(this.inputEl(), {
13171 "up" : function(e){
13172 this.inKeyMode = true;
13176 "down" : function(e){
13177 if(!this.isExpanded()){
13178 this.onTriggerClick();
13180 this.inKeyMode = true;
13185 "enter" : function(e){
13186 // this.onViewClick();
13190 if(this.fireEvent("specialkey", this, e)){
13191 this.onViewClick(false);
13197 "esc" : function(e){
13201 "tab" : function(e){
13204 if(this.fireEvent("specialkey", this, e)){
13205 this.onViewClick(false);
13213 doRelay : function(foo, bar, hname){
13214 if(hname == 'down' || this.scope.isExpanded()){
13215 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13224 this.queryDelay = Math.max(this.queryDelay || 10,
13225 this.mode == 'local' ? 10 : 250);
13228 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13230 if(this.typeAhead){
13231 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13233 if(this.editable !== false){
13234 this.inputEl().on("keyup", this.onKeyUp, this);
13236 if(this.forceSelection){
13237 this.inputEl().on('blur', this.doForce, this);
13241 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13242 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13246 initTickableEvents: function()
13250 if(this.hiddenName){
13252 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13254 this.hiddenField.dom.value =
13255 this.hiddenValue !== undefined ? this.hiddenValue :
13256 this.value !== undefined ? this.value : '';
13258 // prevent input submission
13259 this.el.dom.removeAttribute('name');
13260 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13265 // this.list = this.el.select('ul.dropdown-menu',true).first();
13267 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13268 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13269 if(this.triggerList){
13270 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13273 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13274 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13276 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13277 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13279 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13280 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13282 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13283 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13284 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13287 this.cancelBtn.hide();
13292 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13293 _this.list.setWidth(lw);
13296 this.list.on('mouseover', this.onViewOver, this);
13297 this.list.on('mousemove', this.onViewMove, this);
13299 this.list.on('scroll', this.onViewScroll, this);
13302 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>';
13305 this.view = new Roo.View(this.list, this.tpl, {
13306 singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
13309 //this.view.wrapEl.setDisplayed(false);
13310 this.view.on('click', this.onViewClick, this);
13314 this.store.on('beforeload', this.onBeforeLoad, this);
13315 this.store.on('load', this.onLoad, this);
13316 this.store.on('loadexception', this.onLoadException, this);
13319 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13320 "up" : function(e){
13321 this.inKeyMode = true;
13325 "down" : function(e){
13326 this.inKeyMode = true;
13330 "enter" : function(e){
13331 if(this.fireEvent("specialkey", this, e)){
13332 this.onViewClick(false);
13338 "esc" : function(e){
13339 this.onTickableFooterButtonClick(e, false, false);
13342 "tab" : function(e){
13343 this.fireEvent("specialkey", this, e);
13345 this.onTickableFooterButtonClick(e, false, false);
13352 doRelay : function(e, fn, key){
13353 if(this.scope.isExpanded()){
13354 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13363 this.queryDelay = Math.max(this.queryDelay || 10,
13364 this.mode == 'local' ? 10 : 250);
13367 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13369 if(this.typeAhead){
13370 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13373 if(this.editable !== false){
13374 this.tickableInputEl().on("keyup", this.onKeyUp, this);
13377 this.indicator = this.indicatorEl();
13379 if(this.indicator){
13380 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13381 this.indicator.hide();
13386 onDestroy : function(){
13388 this.view.setStore(null);
13389 this.view.el.removeAllListeners();
13390 this.view.el.remove();
13391 this.view.purgeListeners();
13394 this.list.dom.innerHTML = '';
13398 this.store.un('beforeload', this.onBeforeLoad, this);
13399 this.store.un('load', this.onLoad, this);
13400 this.store.un('loadexception', this.onLoadException, this);
13402 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13406 fireKey : function(e){
13407 if(e.isNavKeyPress() && !this.list.isVisible()){
13408 this.fireEvent("specialkey", this, e);
13413 onResize: function(w, h){
13414 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13416 // if(typeof w != 'number'){
13417 // // we do not handle it!?!?
13420 // var tw = this.trigger.getWidth();
13421 // // tw += this.addicon ? this.addicon.getWidth() : 0;
13422 // // tw += this.editicon ? this.editicon.getWidth() : 0;
13424 // this.inputEl().setWidth( this.adjustWidth('input', x));
13426 // //this.trigger.setStyle('left', x+'px');
13428 // if(this.list && this.listWidth === undefined){
13429 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13430 // this.list.setWidth(lw);
13431 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13439 * Allow or prevent the user from directly editing the field text. If false is passed,
13440 * the user will only be able to select from the items defined in the dropdown list. This method
13441 * is the runtime equivalent of setting the 'editable' config option at config time.
13442 * @param {Boolean} value True to allow the user to directly edit the field text
13444 setEditable : function(value){
13445 if(value == this.editable){
13448 this.editable = value;
13450 this.inputEl().dom.setAttribute('readOnly', true);
13451 this.inputEl().on('mousedown', this.onTriggerClick, this);
13452 this.inputEl().addClass('x-combo-noedit');
13454 this.inputEl().dom.setAttribute('readOnly', false);
13455 this.inputEl().un('mousedown', this.onTriggerClick, this);
13456 this.inputEl().removeClass('x-combo-noedit');
13462 onBeforeLoad : function(combo,opts){
13463 if(!this.hasFocus){
13467 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13469 this.restrictHeight();
13470 this.selectedIndex = -1;
13474 onLoad : function(){
13476 this.hasQuery = false;
13478 if(!this.hasFocus){
13482 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13483 this.loading.hide();
13486 if(this.store.getCount() > 0){
13489 this.restrictHeight();
13490 if(this.lastQuery == this.allQuery){
13491 if(this.editable && !this.tickable){
13492 this.inputEl().dom.select();
13496 !this.selectByValue(this.value, true) &&
13499 !this.store.lastOptions ||
13500 typeof(this.store.lastOptions.add) == 'undefined' ||
13501 this.store.lastOptions.add != true
13504 this.select(0, true);
13507 if(this.autoFocus){
13510 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13511 this.taTask.delay(this.typeAheadDelay);
13515 this.onEmptyResults();
13521 onLoadException : function()
13523 this.hasQuery = false;
13525 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13526 this.loading.hide();
13529 if(this.tickable && this.editable){
13534 // only causes errors at present
13535 //Roo.log(this.store.reader.jsonData);
13536 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13538 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13544 onTypeAhead : function(){
13545 if(this.store.getCount() > 0){
13546 var r = this.store.getAt(0);
13547 var newValue = r.data[this.displayField];
13548 var len = newValue.length;
13549 var selStart = this.getRawValue().length;
13551 if(selStart != len){
13552 this.setRawValue(newValue);
13553 this.selectText(selStart, newValue.length);
13559 onSelect : function(record, index){
13561 if(this.fireEvent('beforeselect', this, record, index) !== false){
13563 this.setFromData(index > -1 ? record.data : false);
13566 this.fireEvent('select', this, record, index);
13571 * Returns the currently selected field value or empty string if no value is set.
13572 * @return {String} value The selected value
13574 getValue : function()
13576 if(Roo.isIOS && this.useNativeIOS){
13577 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13581 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13584 if(this.valueField){
13585 return typeof this.value != 'undefined' ? this.value : '';
13587 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13591 getRawValue : function()
13593 if(Roo.isIOS && this.useNativeIOS){
13594 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13597 var v = this.inputEl().getValue();
13603 * Clears any text/value currently set in the field
13605 clearValue : function(){
13607 if(this.hiddenField){
13608 this.hiddenField.dom.value = '';
13611 this.setRawValue('');
13612 this.lastSelectionText = '';
13613 this.lastData = false;
13615 var close = this.closeTriggerEl();
13626 * Sets the specified value into the field. If the value finds a match, the corresponding record text
13627 * will be displayed in the field. If the value does not match the data value of an existing item,
13628 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13629 * Otherwise the field will be blank (although the value will still be set).
13630 * @param {String} value The value to match
13632 setValue : function(v)
13634 if(Roo.isIOS && this.useNativeIOS){
13635 this.setIOSValue(v);
13645 if(this.valueField){
13646 var r = this.findRecord(this.valueField, v);
13648 text = r.data[this.displayField];
13649 }else if(this.valueNotFoundText !== undefined){
13650 text = this.valueNotFoundText;
13653 this.lastSelectionText = text;
13654 if(this.hiddenField){
13655 this.hiddenField.dom.value = v;
13657 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13660 var close = this.closeTriggerEl();
13663 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13669 * @property {Object} the last set data for the element
13674 * Sets the value of the field based on a object which is related to the record format for the store.
13675 * @param {Object} value the value to set as. or false on reset?
13677 setFromData : function(o){
13684 var dv = ''; // display value
13685 var vv = ''; // value value..
13687 if (this.displayField) {
13688 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13690 // this is an error condition!!!
13691 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
13694 if(this.valueField){
13695 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13698 var close = this.closeTriggerEl();
13701 (vv.length || vv * 1 > 0) ? close.show() : close.hide();
13704 if(this.hiddenField){
13705 this.hiddenField.dom.value = vv;
13707 this.lastSelectionText = dv;
13708 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13712 // no hidden field.. - we store the value in 'value', but still display
13713 // display field!!!!
13714 this.lastSelectionText = dv;
13715 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13722 reset : function(){
13723 // overridden so that last data is reset..
13730 this.setValue(this.originalValue);
13731 //this.clearInvalid();
13732 this.lastData = false;
13734 this.view.clearSelections();
13740 findRecord : function(prop, value){
13742 if(this.store.getCount() > 0){
13743 this.store.each(function(r){
13744 if(r.data[prop] == value){
13754 getName: function()
13756 // returns hidden if it's set..
13757 if (!this.rendered) {return ''};
13758 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
13762 onViewMove : function(e, t){
13763 this.inKeyMode = false;
13767 onViewOver : function(e, t){
13768 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13771 var item = this.view.findItemFromChild(t);
13774 var index = this.view.indexOf(item);
13775 this.select(index, false);
13780 onViewClick : function(view, doFocus, el, e)
13782 var index = this.view.getSelectedIndexes()[0];
13784 var r = this.store.getAt(index);
13788 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13795 Roo.each(this.tickItems, function(v,k){
13797 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13799 _this.tickItems.splice(k, 1);
13801 if(typeof(e) == 'undefined' && view == false){
13802 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13814 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13815 this.tickItems.push(r.data);
13818 if(typeof(e) == 'undefined' && view == false){
13819 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13826 this.onSelect(r, index);
13828 if(doFocus !== false && !this.blockFocus){
13829 this.inputEl().focus();
13834 restrictHeight : function(){
13835 //this.innerList.dom.style.height = '';
13836 //var inner = this.innerList.dom;
13837 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13838 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13839 //this.list.beginUpdate();
13840 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13841 this.list.alignTo(this.inputEl(), this.listAlign);
13842 this.list.alignTo(this.inputEl(), this.listAlign);
13843 //this.list.endUpdate();
13847 onEmptyResults : function(){
13849 if(this.tickable && this.editable){
13850 this.restrictHeight();
13858 * Returns true if the dropdown list is expanded, else false.
13860 isExpanded : function(){
13861 return this.list.isVisible();
13865 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13866 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13867 * @param {String} value The data value of the item to select
13868 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13869 * selected item if it is not currently in view (defaults to true)
13870 * @return {Boolean} True if the value matched an item in the list, else false
13872 selectByValue : function(v, scrollIntoView){
13873 if(v !== undefined && v !== null){
13874 var r = this.findRecord(this.valueField || this.displayField, v);
13876 this.select(this.store.indexOf(r), scrollIntoView);
13884 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13885 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13886 * @param {Number} index The zero-based index of the list item to select
13887 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13888 * selected item if it is not currently in view (defaults to true)
13890 select : function(index, scrollIntoView){
13891 this.selectedIndex = index;
13892 this.view.select(index);
13893 if(scrollIntoView !== false){
13894 var el = this.view.getNode(index);
13896 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13899 this.list.scrollChildIntoView(el, false);
13905 selectNext : function(){
13906 var ct = this.store.getCount();
13908 if(this.selectedIndex == -1){
13910 }else if(this.selectedIndex < ct-1){
13911 this.select(this.selectedIndex+1);
13917 selectPrev : function(){
13918 var ct = this.store.getCount();
13920 if(this.selectedIndex == -1){
13922 }else if(this.selectedIndex != 0){
13923 this.select(this.selectedIndex-1);
13929 onKeyUp : function(e){
13930 if(this.editable !== false && !e.isSpecialKey()){
13931 this.lastKey = e.getKey();
13932 this.dqTask.delay(this.queryDelay);
13937 validateBlur : function(){
13938 return !this.list || !this.list.isVisible();
13942 initQuery : function(){
13944 var v = this.getRawValue();
13946 if(this.tickable && this.editable){
13947 v = this.tickableInputEl().getValue();
13954 doForce : function(){
13955 if(this.inputEl().dom.value.length > 0){
13956 this.inputEl().dom.value =
13957 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
13963 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
13964 * query allowing the query action to be canceled if needed.
13965 * @param {String} query The SQL query to execute
13966 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
13967 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
13968 * saved in the current store (defaults to false)
13970 doQuery : function(q, forceAll){
13972 if(q === undefined || q === null){
13977 forceAll: forceAll,
13981 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
13986 forceAll = qe.forceAll;
13987 if(forceAll === true || (q.length >= this.minChars)){
13989 this.hasQuery = true;
13991 if(this.lastQuery != q || this.alwaysQuery){
13992 this.lastQuery = q;
13993 if(this.mode == 'local'){
13994 this.selectedIndex = -1;
13996 this.store.clearFilter();
13999 if(this.specialFilter){
14000 this.fireEvent('specialfilter', this);
14005 this.store.filter(this.displayField, q);
14008 this.store.fireEvent("datachanged", this.store);
14015 this.store.baseParams[this.queryParam] = q;
14017 var options = {params : this.getParams(q)};
14020 options.add = true;
14021 options.params.start = this.page * this.pageSize;
14024 this.store.load(options);
14027 * this code will make the page width larger, at the beginning, the list not align correctly,
14028 * we should expand the list on onLoad
14029 * so command out it
14034 this.selectedIndex = -1;
14039 this.loadNext = false;
14043 getParams : function(q){
14045 //p[this.queryParam] = q;
14049 p.limit = this.pageSize;
14055 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14057 collapse : function(){
14058 if(!this.isExpanded()){
14064 this.hasFocus = false;
14068 this.cancelBtn.hide();
14069 this.trigger.show();
14072 this.tickableInputEl().dom.value = '';
14073 this.tickableInputEl().blur();
14078 Roo.get(document).un('mousedown', this.collapseIf, this);
14079 Roo.get(document).un('mousewheel', this.collapseIf, this);
14080 if (!this.editable) {
14081 Roo.get(document).un('keydown', this.listKeyPress, this);
14083 this.fireEvent('collapse', this);
14089 collapseIf : function(e){
14090 var in_combo = e.within(this.el);
14091 var in_list = e.within(this.list);
14092 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14094 if (in_combo || in_list || is_list) {
14095 //e.stopPropagation();
14100 this.onTickableFooterButtonClick(e, false, false);
14108 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14110 expand : function(){
14112 if(this.isExpanded() || !this.hasFocus){
14116 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14117 this.list.setWidth(lw);
14123 this.restrictHeight();
14127 this.tickItems = Roo.apply([], this.item);
14130 this.cancelBtn.show();
14131 this.trigger.hide();
14134 this.tickableInputEl().focus();
14139 Roo.get(document).on('mousedown', this.collapseIf, this);
14140 Roo.get(document).on('mousewheel', this.collapseIf, this);
14141 if (!this.editable) {
14142 Roo.get(document).on('keydown', this.listKeyPress, this);
14145 this.fireEvent('expand', this);
14149 // Implements the default empty TriggerField.onTriggerClick function
14150 onTriggerClick : function(e)
14152 Roo.log('trigger click');
14154 if(this.disabled || !this.triggerList){
14159 this.loadNext = false;
14161 if(this.isExpanded()){
14163 if (!this.blockFocus) {
14164 this.inputEl().focus();
14168 this.hasFocus = true;
14169 if(this.triggerAction == 'all') {
14170 this.doQuery(this.allQuery, true);
14172 this.doQuery(this.getRawValue());
14174 if (!this.blockFocus) {
14175 this.inputEl().focus();
14180 onTickableTriggerClick : function(e)
14187 this.loadNext = false;
14188 this.hasFocus = true;
14190 if(this.triggerAction == 'all') {
14191 this.doQuery(this.allQuery, true);
14193 this.doQuery(this.getRawValue());
14197 onSearchFieldClick : function(e)
14199 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14200 this.onTickableFooterButtonClick(e, false, false);
14204 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14209 this.loadNext = false;
14210 this.hasFocus = true;
14212 if(this.triggerAction == 'all') {
14213 this.doQuery(this.allQuery, true);
14215 this.doQuery(this.getRawValue());
14219 listKeyPress : function(e)
14221 //Roo.log('listkeypress');
14222 // scroll to first matching element based on key pres..
14223 if (e.isSpecialKey()) {
14226 var k = String.fromCharCode(e.getKey()).toUpperCase();
14229 var csel = this.view.getSelectedNodes();
14230 var cselitem = false;
14232 var ix = this.view.indexOf(csel[0]);
14233 cselitem = this.store.getAt(ix);
14234 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14240 this.store.each(function(v) {
14242 // start at existing selection.
14243 if (cselitem.id == v.id) {
14249 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14250 match = this.store.indexOf(v);
14256 if (match === false) {
14257 return true; // no more action?
14260 this.view.select(match);
14261 var sn = Roo.get(this.view.getSelectedNodes()[0]);
14262 sn.scrollIntoView(sn.dom.parentNode, false);
14265 onViewScroll : function(e, t){
14267 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){
14271 this.hasQuery = true;
14273 this.loading = this.list.select('.loading', true).first();
14275 if(this.loading === null){
14276 this.list.createChild({
14278 cls: 'loading roo-select2-more-results roo-select2-active',
14279 html: 'Loading more results...'
14282 this.loading = this.list.select('.loading', true).first();
14284 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14286 this.loading.hide();
14289 this.loading.show();
14294 this.loadNext = true;
14296 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14301 addItem : function(o)
14303 var dv = ''; // display value
14305 if (this.displayField) {
14306 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14308 // this is an error condition!!!
14309 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14316 var choice = this.choices.createChild({
14318 cls: 'roo-select2-search-choice',
14327 cls: 'roo-select2-search-choice-close fa fa-times',
14332 }, this.searchField);
14334 var close = choice.select('a.roo-select2-search-choice-close', true).first();
14336 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14344 this.inputEl().dom.value = '';
14349 onRemoveItem : function(e, _self, o)
14351 e.preventDefault();
14353 this.lastItem = Roo.apply([], this.item);
14355 var index = this.item.indexOf(o.data) * 1;
14358 Roo.log('not this item?!');
14362 this.item.splice(index, 1);
14367 this.fireEvent('remove', this, e);
14373 syncValue : function()
14375 if(!this.item.length){
14382 Roo.each(this.item, function(i){
14383 if(_this.valueField){
14384 value.push(i[_this.valueField]);
14391 this.value = value.join(',');
14393 if(this.hiddenField){
14394 this.hiddenField.dom.value = this.value;
14397 this.store.fireEvent("datachanged", this.store);
14402 clearItem : function()
14404 if(!this.multiple){
14410 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14418 if(this.tickable && !Roo.isTouch){
14419 this.view.refresh();
14423 inputEl: function ()
14425 if(Roo.isIOS && this.useNativeIOS){
14426 return this.el.select('select.roo-ios-select', true).first();
14429 if(Roo.isTouch && this.mobileTouchView){
14430 return this.el.select('input.form-control',true).first();
14434 return this.searchField;
14437 return this.el.select('input.form-control',true).first();
14440 onTickableFooterButtonClick : function(e, btn, el)
14442 e.preventDefault();
14444 this.lastItem = Roo.apply([], this.item);
14446 if(btn && btn.name == 'cancel'){
14447 this.tickItems = Roo.apply([], this.item);
14456 Roo.each(this.tickItems, function(o){
14464 validate : function()
14466 var v = this.getRawValue();
14469 v = this.getValue();
14472 if(this.disabled || this.allowBlank || v.length){
14477 this.markInvalid();
14481 tickableInputEl : function()
14483 if(!this.tickable || !this.editable){
14484 return this.inputEl();
14487 return this.inputEl().select('.roo-select2-search-field-input', true).first();
14491 getAutoCreateTouchView : function()
14496 cls: 'form-group' //input-group
14502 type : this.inputType,
14503 cls : 'form-control x-combo-noedit',
14504 autocomplete: 'new-password',
14505 placeholder : this.placeholder || '',
14510 input.name = this.name;
14514 input.cls += ' input-' + this.size;
14517 if (this.disabled) {
14518 input.disabled = true;
14529 inputblock.cls += ' input-group';
14531 inputblock.cn.unshift({
14533 cls : 'input-group-addon',
14538 if(this.removable && !this.multiple){
14539 inputblock.cls += ' roo-removable';
14541 inputblock.cn.push({
14544 cls : 'roo-combo-removable-btn close'
14548 if(this.hasFeedback && !this.allowBlank){
14550 inputblock.cls += ' has-feedback';
14552 inputblock.cn.push({
14554 cls: 'glyphicon form-control-feedback'
14561 inputblock.cls += (this.before) ? '' : ' input-group';
14563 inputblock.cn.push({
14565 cls : 'input-group-addon',
14576 cls: 'form-hidden-field'
14590 cls: 'form-hidden-field'
14594 cls: 'roo-select2-choices',
14598 cls: 'roo-select2-search-field',
14611 cls: 'roo-select2-container input-group roo-touchview-combobox ',
14617 if(!this.multiple && this.showToggleBtn){
14624 if (this.caret != false) {
14627 cls: 'fa fa-' + this.caret
14634 cls : 'input-group-addon btn dropdown-toggle',
14639 cls: 'combobox-clear',
14653 combobox.cls += ' roo-select2-container-multi';
14656 var align = this.labelAlign || this.parentLabelAlign();
14658 if (align ==='left' && this.fieldLabel.length) {
14663 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14664 tooltip : 'This field is required'
14668 cls : 'control-label',
14669 html : this.fieldLabel
14680 var labelCfg = cfg.cn[1];
14681 var contentCfg = cfg.cn[2];
14684 if(this.indicatorpos == 'right'){
14688 cls : 'control-label',
14689 html : this.fieldLabel,
14693 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14694 tooltip : 'This field is required'
14707 labelCfg = cfg.cn[0];
14708 contentCfg = cfg.cn[2];
14710 if(this.labelWidth > 12){
14711 labelCfg.style = "width: " + this.labelWidth + 'px';
14714 if(this.labelWidth < 13 && this.labelmd == 0){
14715 this.labelmd = this.labelWidth;
14718 if(this.labellg > 0){
14719 labelCfg.cls += ' col-lg-' + this.labellg;
14720 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14723 if(this.labelmd > 0){
14724 labelCfg.cls += ' col-md-' + this.labelmd;
14725 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14728 if(this.labelsm > 0){
14729 labelCfg.cls += ' col-sm-' + this.labelsm;
14730 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14733 if(this.labelxs > 0){
14734 labelCfg.cls += ' col-xs-' + this.labelxs;
14735 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14739 } else if ( this.fieldLabel.length) {
14743 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14744 tooltip : 'This field is required'
14748 cls : 'control-label',
14749 html : this.fieldLabel
14760 if(this.indicatorpos == 'right'){
14764 cls : 'control-label',
14765 html : this.fieldLabel,
14769 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14770 tooltip : 'This field is required'
14787 var settings = this;
14789 ['xs','sm','md','lg'].map(function(size){
14790 if (settings[size]) {
14791 cfg.cls += ' col-' + size + '-' + settings[size];
14798 initTouchView : function()
14800 this.renderTouchView();
14802 this.touchViewEl.on('scroll', function(){
14803 this.el.dom.scrollTop = 0;
14806 this.originalValue = this.getValue();
14808 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14810 this.inputEl().on("click", this.showTouchView, this);
14811 if (this.triggerEl) {
14812 this.triggerEl.on("click", this.showTouchView, this);
14816 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14817 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14819 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14821 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14822 this.store.on('load', this.onTouchViewLoad, this);
14823 this.store.on('loadexception', this.onTouchViewLoadException, this);
14825 if(this.hiddenName){
14827 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14829 this.hiddenField.dom.value =
14830 this.hiddenValue !== undefined ? this.hiddenValue :
14831 this.value !== undefined ? this.value : '';
14833 this.el.dom.removeAttribute('name');
14834 this.hiddenField.dom.setAttribute('name', this.hiddenName);
14838 this.choices = this.el.select('ul.roo-select2-choices', true).first();
14839 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14842 if(this.removable && !this.multiple){
14843 var close = this.closeTriggerEl();
14845 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14846 close.on('click', this.removeBtnClick, this, close);
14850 * fix the bug in Safari iOS8
14852 this.inputEl().on("focus", function(e){
14853 document.activeElement.blur();
14861 renderTouchView : function()
14863 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14864 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14866 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14867 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14869 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14870 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14871 this.touchViewBodyEl.setStyle('overflow', 'auto');
14873 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14874 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14876 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14877 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14881 showTouchView : function()
14887 this.touchViewHeaderEl.hide();
14889 if(this.modalTitle.length){
14890 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14891 this.touchViewHeaderEl.show();
14894 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
14895 this.touchViewEl.show();
14897 this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
14898 this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14899 Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14901 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14903 if(this.modalTitle.length){
14904 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14907 this.touchViewBodyEl.setHeight(bodyHeight);
14911 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14913 this.touchViewEl.addClass('in');
14916 this.doTouchViewQuery();
14920 hideTouchView : function()
14922 this.touchViewEl.removeClass('in');
14926 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14928 this.touchViewEl.setStyle('display', 'none');
14933 setTouchViewValue : function()
14940 Roo.each(this.tickItems, function(o){
14945 this.hideTouchView();
14948 doTouchViewQuery : function()
14957 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
14961 if(!this.alwaysQuery || this.mode == 'local'){
14962 this.onTouchViewLoad();
14969 onTouchViewBeforeLoad : function(combo,opts)
14975 onTouchViewLoad : function()
14977 if(this.store.getCount() < 1){
14978 this.onTouchViewEmptyResults();
14982 this.clearTouchView();
14984 var rawValue = this.getRawValue();
14986 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
14988 this.tickItems = [];
14990 this.store.data.each(function(d, rowIndex){
14991 var row = this.touchViewListGroup.createChild(template);
14993 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
14994 row.addClass(d.data.cls);
14997 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15000 html : d.data[this.displayField]
15003 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15004 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15007 row.removeClass('selected');
15008 if(!this.multiple && this.valueField &&
15009 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15012 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15013 row.addClass('selected');
15016 if(this.multiple && this.valueField &&
15017 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15021 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15022 this.tickItems.push(d.data);
15025 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15029 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15031 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15033 if(this.modalTitle.length){
15034 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15037 var listHeight = this.touchViewListGroup.getHeight();
15041 if(firstChecked && listHeight > bodyHeight){
15042 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15047 onTouchViewLoadException : function()
15049 this.hideTouchView();
15052 onTouchViewEmptyResults : function()
15054 this.clearTouchView();
15056 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15058 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15062 clearTouchView : function()
15064 this.touchViewListGroup.dom.innerHTML = '';
15067 onTouchViewClick : function(e, el, o)
15069 e.preventDefault();
15072 var rowIndex = o.rowIndex;
15074 var r = this.store.getAt(rowIndex);
15076 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15078 if(!this.multiple){
15079 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15080 c.dom.removeAttribute('checked');
15083 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15085 this.setFromData(r.data);
15087 var close = this.closeTriggerEl();
15093 this.hideTouchView();
15095 this.fireEvent('select', this, r, rowIndex);
15100 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15101 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15102 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15106 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15107 this.addItem(r.data);
15108 this.tickItems.push(r.data);
15112 getAutoCreateNativeIOS : function()
15115 cls: 'form-group' //input-group,
15120 cls : 'roo-ios-select'
15124 combobox.name = this.name;
15127 if (this.disabled) {
15128 combobox.disabled = true;
15131 var settings = this;
15133 ['xs','sm','md','lg'].map(function(size){
15134 if (settings[size]) {
15135 cfg.cls += ' col-' + size + '-' + settings[size];
15145 initIOSView : function()
15147 this.store.on('load', this.onIOSViewLoad, this);
15152 onIOSViewLoad : function()
15154 if(this.store.getCount() < 1){
15158 this.clearIOSView();
15160 if(this.allowBlank) {
15162 var default_text = '-- SELECT --';
15164 var opt = this.inputEl().createChild({
15167 html : default_text
15171 o[this.valueField] = 0;
15172 o[this.displayField] = default_text;
15174 this.ios_options.push({
15181 this.store.data.each(function(d, rowIndex){
15185 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15186 html = d.data[this.displayField];
15191 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15192 value = d.data[this.valueField];
15201 if(this.value == d.data[this.valueField]){
15202 option['selected'] = true;
15205 var opt = this.inputEl().createChild(option);
15207 this.ios_options.push({
15214 this.inputEl().on('change', function(){
15215 this.fireEvent('select', this);
15220 clearIOSView: function()
15222 this.inputEl().dom.innerHTML = '';
15224 this.ios_options = [];
15227 setIOSValue: function(v)
15231 if(!this.ios_options){
15235 Roo.each(this.ios_options, function(opts){
15237 opts.el.dom.removeAttribute('selected');
15239 if(opts.data[this.valueField] != v){
15243 opts.el.dom.setAttribute('selected', true);
15249 * @cfg {Boolean} grow
15253 * @cfg {Number} growMin
15257 * @cfg {Number} growMax
15266 Roo.apply(Roo.bootstrap.ComboBox, {
15270 cls: 'modal-header',
15292 cls: 'list-group-item',
15296 cls: 'roo-combobox-list-group-item-value'
15300 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15314 listItemCheckbox : {
15316 cls: 'list-group-item',
15320 cls: 'roo-combobox-list-group-item-value'
15324 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15340 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15345 cls: 'modal-footer',
15353 cls: 'col-xs-6 text-left',
15356 cls: 'btn btn-danger roo-touch-view-cancel',
15362 cls: 'col-xs-6 text-right',
15365 cls: 'btn btn-success roo-touch-view-ok',
15376 Roo.apply(Roo.bootstrap.ComboBox, {
15378 touchViewTemplate : {
15380 cls: 'modal fade roo-combobox-touch-view',
15384 cls: 'modal-dialog',
15385 style : 'position:fixed', // we have to fix position....
15389 cls: 'modal-content',
15391 Roo.bootstrap.ComboBox.header,
15392 Roo.bootstrap.ComboBox.body,
15393 Roo.bootstrap.ComboBox.footer
15402 * Ext JS Library 1.1.1
15403 * Copyright(c) 2006-2007, Ext JS, LLC.
15405 * Originally Released Under LGPL - original licence link has changed is not relivant.
15408 * <script type="text/javascript">
15413 * @extends Roo.util.Observable
15414 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
15415 * This class also supports single and multi selection modes. <br>
15416 * Create a data model bound view:
15418 var store = new Roo.data.Store(...);
15420 var view = new Roo.View({
15422 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
15424 singleSelect: true,
15425 selectedClass: "ydataview-selected",
15429 // listen for node click?
15430 view.on("click", function(vw, index, node, e){
15431 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15435 dataModel.load("foobar.xml");
15437 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15439 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15440 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15442 * Note: old style constructor is still suported (container, template, config)
15445 * Create a new View
15446 * @param {Object} config The config object
15449 Roo.View = function(config, depreciated_tpl, depreciated_config){
15451 this.parent = false;
15453 if (typeof(depreciated_tpl) == 'undefined') {
15454 // new way.. - universal constructor.
15455 Roo.apply(this, config);
15456 this.el = Roo.get(this.el);
15459 this.el = Roo.get(config);
15460 this.tpl = depreciated_tpl;
15461 Roo.apply(this, depreciated_config);
15463 this.wrapEl = this.el.wrap().wrap();
15464 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15467 if(typeof(this.tpl) == "string"){
15468 this.tpl = new Roo.Template(this.tpl);
15470 // support xtype ctors..
15471 this.tpl = new Roo.factory(this.tpl, Roo);
15475 this.tpl.compile();
15480 * @event beforeclick
15481 * Fires before a click is processed. Returns false to cancel the default action.
15482 * @param {Roo.View} this
15483 * @param {Number} index The index of the target node
15484 * @param {HTMLElement} node The target node
15485 * @param {Roo.EventObject} e The raw event object
15487 "beforeclick" : true,
15490 * Fires when a template node is clicked.
15491 * @param {Roo.View} this
15492 * @param {Number} index The index of the target node
15493 * @param {HTMLElement} node The target node
15494 * @param {Roo.EventObject} e The raw event object
15499 * Fires when a template node is double clicked.
15500 * @param {Roo.View} this
15501 * @param {Number} index The index of the target node
15502 * @param {HTMLElement} node The target node
15503 * @param {Roo.EventObject} e The raw event object
15507 * @event contextmenu
15508 * Fires when a template node is right clicked.
15509 * @param {Roo.View} this
15510 * @param {Number} index The index of the target node
15511 * @param {HTMLElement} node The target node
15512 * @param {Roo.EventObject} e The raw event object
15514 "contextmenu" : true,
15516 * @event selectionchange
15517 * Fires when the selected nodes change.
15518 * @param {Roo.View} this
15519 * @param {Array} selections Array of the selected nodes
15521 "selectionchange" : true,
15524 * @event beforeselect
15525 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15526 * @param {Roo.View} this
15527 * @param {HTMLElement} node The node to be selected
15528 * @param {Array} selections Array of currently selected nodes
15530 "beforeselect" : true,
15532 * @event preparedata
15533 * Fires on every row to render, to allow you to change the data.
15534 * @param {Roo.View} this
15535 * @param {Object} data to be rendered (change this)
15537 "preparedata" : true
15545 "click": this.onClick,
15546 "dblclick": this.onDblClick,
15547 "contextmenu": this.onContextMenu,
15551 this.selections = [];
15553 this.cmp = new Roo.CompositeElementLite([]);
15555 this.store = Roo.factory(this.store, Roo.data);
15556 this.setStore(this.store, true);
15559 if ( this.footer && this.footer.xtype) {
15561 var fctr = this.wrapEl.appendChild(document.createElement("div"));
15563 this.footer.dataSource = this.store;
15564 this.footer.container = fctr;
15565 this.footer = Roo.factory(this.footer, Roo);
15566 fctr.insertFirst(this.el);
15568 // this is a bit insane - as the paging toolbar seems to detach the el..
15569 // dom.parentNode.parentNode.parentNode
15570 // they get detached?
15574 Roo.View.superclass.constructor.call(this);
15579 Roo.extend(Roo.View, Roo.util.Observable, {
15582 * @cfg {Roo.data.Store} store Data store to load data from.
15587 * @cfg {String|Roo.Element} el The container element.
15592 * @cfg {String|Roo.Template} tpl The template used by this View
15596 * @cfg {String} dataName the named area of the template to use as the data area
15597 * Works with domtemplates roo-name="name"
15601 * @cfg {String} selectedClass The css class to add to selected nodes
15603 selectedClass : "x-view-selected",
15605 * @cfg {String} emptyText The empty text to show when nothing is loaded.
15610 * @cfg {String} text to display on mask (default Loading)
15614 * @cfg {Boolean} multiSelect Allow multiple selection
15616 multiSelect : false,
15618 * @cfg {Boolean} singleSelect Allow single selection
15620 singleSelect: false,
15623 * @cfg {Boolean} toggleSelect - selecting
15625 toggleSelect : false,
15628 * @cfg {Boolean} tickable - selecting
15633 * Returns the element this view is bound to.
15634 * @return {Roo.Element}
15636 getEl : function(){
15637 return this.wrapEl;
15643 * Refreshes the view. - called by datachanged on the store. - do not call directly.
15645 refresh : function(){
15646 //Roo.log('refresh');
15649 // if we are using something like 'domtemplate', then
15650 // the what gets used is:
15651 // t.applySubtemplate(NAME, data, wrapping data..)
15652 // the outer template then get' applied with
15653 // the store 'extra data'
15654 // and the body get's added to the
15655 // roo-name="data" node?
15656 // <span class='roo-tpl-{name}'></span> ?????
15660 this.clearSelections();
15661 this.el.update("");
15663 var records = this.store.getRange();
15664 if(records.length < 1) {
15666 // is this valid?? = should it render a template??
15668 this.el.update(this.emptyText);
15672 if (this.dataName) {
15673 this.el.update(t.apply(this.store.meta)); //????
15674 el = this.el.child('.roo-tpl-' + this.dataName);
15677 for(var i = 0, len = records.length; i < len; i++){
15678 var data = this.prepareData(records[i].data, i, records[i]);
15679 this.fireEvent("preparedata", this, data, i, records[i]);
15681 var d = Roo.apply({}, data);
15684 Roo.apply(d, {'roo-id' : Roo.id()});
15688 Roo.each(this.parent.item, function(item){
15689 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15692 Roo.apply(d, {'roo-data-checked' : 'checked'});
15696 html[html.length] = Roo.util.Format.trim(
15698 t.applySubtemplate(this.dataName, d, this.store.meta) :
15705 el.update(html.join(""));
15706 this.nodes = el.dom.childNodes;
15707 this.updateIndexes(0);
15712 * Function to override to reformat the data that is sent to
15713 * the template for each node.
15714 * DEPRICATED - use the preparedata event handler.
15715 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15716 * a JSON object for an UpdateManager bound view).
15718 prepareData : function(data, index, record)
15720 this.fireEvent("preparedata", this, data, index, record);
15724 onUpdate : function(ds, record){
15725 // Roo.log('on update');
15726 this.clearSelections();
15727 var index = this.store.indexOf(record);
15728 var n = this.nodes[index];
15729 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
15730 n.parentNode.removeChild(n);
15731 this.updateIndexes(index, index);
15737 onAdd : function(ds, records, index)
15739 //Roo.log(['on Add', ds, records, index] );
15740 this.clearSelections();
15741 if(this.nodes.length == 0){
15745 var n = this.nodes[index];
15746 for(var i = 0, len = records.length; i < len; i++){
15747 var d = this.prepareData(records[i].data, i, records[i]);
15749 this.tpl.insertBefore(n, d);
15752 this.tpl.append(this.el, d);
15755 this.updateIndexes(index);
15758 onRemove : function(ds, record, index){
15759 // Roo.log('onRemove');
15760 this.clearSelections();
15761 var el = this.dataName ?
15762 this.el.child('.roo-tpl-' + this.dataName) :
15765 el.dom.removeChild(this.nodes[index]);
15766 this.updateIndexes(index);
15770 * Refresh an individual node.
15771 * @param {Number} index
15773 refreshNode : function(index){
15774 this.onUpdate(this.store, this.store.getAt(index));
15777 updateIndexes : function(startIndex, endIndex){
15778 var ns = this.nodes;
15779 startIndex = startIndex || 0;
15780 endIndex = endIndex || ns.length - 1;
15781 for(var i = startIndex; i <= endIndex; i++){
15782 ns[i].nodeIndex = i;
15787 * Changes the data store this view uses and refresh the view.
15788 * @param {Store} store
15790 setStore : function(store, initial){
15791 if(!initial && this.store){
15792 this.store.un("datachanged", this.refresh);
15793 this.store.un("add", this.onAdd);
15794 this.store.un("remove", this.onRemove);
15795 this.store.un("update", this.onUpdate);
15796 this.store.un("clear", this.refresh);
15797 this.store.un("beforeload", this.onBeforeLoad);
15798 this.store.un("load", this.onLoad);
15799 this.store.un("loadexception", this.onLoad);
15803 store.on("datachanged", this.refresh, this);
15804 store.on("add", this.onAdd, this);
15805 store.on("remove", this.onRemove, this);
15806 store.on("update", this.onUpdate, this);
15807 store.on("clear", this.refresh, this);
15808 store.on("beforeload", this.onBeforeLoad, this);
15809 store.on("load", this.onLoad, this);
15810 store.on("loadexception", this.onLoad, this);
15818 * onbeforeLoad - masks the loading area.
15821 onBeforeLoad : function(store,opts)
15823 //Roo.log('onBeforeLoad');
15825 this.el.update("");
15827 this.el.mask(this.mask ? this.mask : "Loading" );
15829 onLoad : function ()
15836 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15837 * @param {HTMLElement} node
15838 * @return {HTMLElement} The template node
15840 findItemFromChild : function(node){
15841 var el = this.dataName ?
15842 this.el.child('.roo-tpl-' + this.dataName,true) :
15845 if(!node || node.parentNode == el){
15848 var p = node.parentNode;
15849 while(p && p != el){
15850 if(p.parentNode == el){
15859 onClick : function(e){
15860 var item = this.findItemFromChild(e.getTarget());
15862 var index = this.indexOf(item);
15863 if(this.onItemClick(item, index, e) !== false){
15864 this.fireEvent("click", this, index, item, e);
15867 this.clearSelections();
15872 onContextMenu : function(e){
15873 var item = this.findItemFromChild(e.getTarget());
15875 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15880 onDblClick : function(e){
15881 var item = this.findItemFromChild(e.getTarget());
15883 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15887 onItemClick : function(item, index, e)
15889 if(this.fireEvent("beforeclick", this, index, item, e) === false){
15892 if (this.toggleSelect) {
15893 var m = this.isSelected(item) ? 'unselect' : 'select';
15896 _t[m](item, true, false);
15899 if(this.multiSelect || this.singleSelect){
15900 if(this.multiSelect && e.shiftKey && this.lastSelection){
15901 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
15903 this.select(item, this.multiSelect && e.ctrlKey);
15904 this.lastSelection = item;
15907 if(!this.tickable){
15908 e.preventDefault();
15916 * Get the number of selected nodes.
15919 getSelectionCount : function(){
15920 return this.selections.length;
15924 * Get the currently selected nodes.
15925 * @return {Array} An array of HTMLElements
15927 getSelectedNodes : function(){
15928 return this.selections;
15932 * Get the indexes of the selected nodes.
15935 getSelectedIndexes : function(){
15936 var indexes = [], s = this.selections;
15937 for(var i = 0, len = s.length; i < len; i++){
15938 indexes.push(s[i].nodeIndex);
15944 * Clear all selections
15945 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
15947 clearSelections : function(suppressEvent){
15948 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
15949 this.cmp.elements = this.selections;
15950 this.cmp.removeClass(this.selectedClass);
15951 this.selections = [];
15952 if(!suppressEvent){
15953 this.fireEvent("selectionchange", this, this.selections);
15959 * Returns true if the passed node is selected
15960 * @param {HTMLElement/Number} node The node or node index
15961 * @return {Boolean}
15963 isSelected : function(node){
15964 var s = this.selections;
15968 node = this.getNode(node);
15969 return s.indexOf(node) !== -1;
15974 * @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
15975 * @param {Boolean} keepExisting (optional) true to keep existing selections
15976 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15978 select : function(nodeInfo, keepExisting, suppressEvent){
15979 if(nodeInfo instanceof Array){
15981 this.clearSelections(true);
15983 for(var i = 0, len = nodeInfo.length; i < len; i++){
15984 this.select(nodeInfo[i], true, true);
15988 var node = this.getNode(nodeInfo);
15989 if(!node || this.isSelected(node)){
15990 return; // already selected.
15993 this.clearSelections(true);
15996 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
15997 Roo.fly(node).addClass(this.selectedClass);
15998 this.selections.push(node);
15999 if(!suppressEvent){
16000 this.fireEvent("selectionchange", this, this.selections);
16008 * @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
16009 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16010 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16012 unselect : function(nodeInfo, keepExisting, suppressEvent)
16014 if(nodeInfo instanceof Array){
16015 Roo.each(this.selections, function(s) {
16016 this.unselect(s, nodeInfo);
16020 var node = this.getNode(nodeInfo);
16021 if(!node || !this.isSelected(node)){
16022 //Roo.log("not selected");
16023 return; // not selected.
16027 Roo.each(this.selections, function(s) {
16029 Roo.fly(node).removeClass(this.selectedClass);
16036 this.selections= ns;
16037 this.fireEvent("selectionchange", this, this.selections);
16041 * Gets a template node.
16042 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16043 * @return {HTMLElement} The node or null if it wasn't found
16045 getNode : function(nodeInfo){
16046 if(typeof nodeInfo == "string"){
16047 return document.getElementById(nodeInfo);
16048 }else if(typeof nodeInfo == "number"){
16049 return this.nodes[nodeInfo];
16055 * Gets a range template nodes.
16056 * @param {Number} startIndex
16057 * @param {Number} endIndex
16058 * @return {Array} An array of nodes
16060 getNodes : function(start, end){
16061 var ns = this.nodes;
16062 start = start || 0;
16063 end = typeof end == "undefined" ? ns.length - 1 : end;
16066 for(var i = start; i <= end; i++){
16070 for(var i = start; i >= end; i--){
16078 * Finds the index of the passed node
16079 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16080 * @return {Number} The index of the node or -1
16082 indexOf : function(node){
16083 node = this.getNode(node);
16084 if(typeof node.nodeIndex == "number"){
16085 return node.nodeIndex;
16087 var ns = this.nodes;
16088 for(var i = 0, len = ns.length; i < len; i++){
16099 * based on jquery fullcalendar
16103 Roo.bootstrap = Roo.bootstrap || {};
16105 * @class Roo.bootstrap.Calendar
16106 * @extends Roo.bootstrap.Component
16107 * Bootstrap Calendar class
16108 * @cfg {Boolean} loadMask (true|false) default false
16109 * @cfg {Object} header generate the user specific header of the calendar, default false
16112 * Create a new Container
16113 * @param {Object} config The config object
16118 Roo.bootstrap.Calendar = function(config){
16119 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16123 * Fires when a date is selected
16124 * @param {DatePicker} this
16125 * @param {Date} date The selected date
16129 * @event monthchange
16130 * Fires when the displayed month changes
16131 * @param {DatePicker} this
16132 * @param {Date} date The selected month
16134 'monthchange': true,
16136 * @event evententer
16137 * Fires when mouse over an event
16138 * @param {Calendar} this
16139 * @param {event} Event
16141 'evententer': true,
16143 * @event eventleave
16144 * Fires when the mouse leaves an
16145 * @param {Calendar} this
16148 'eventleave': true,
16150 * @event eventclick
16151 * Fires when the mouse click an
16152 * @param {Calendar} this
16161 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
16164 * @cfg {Number} startDay
16165 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16173 getAutoCreate : function(){
16176 var fc_button = function(name, corner, style, content ) {
16177 return Roo.apply({},{
16179 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
16181 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16184 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16195 style : 'width:100%',
16202 cls : 'fc-header-left',
16204 fc_button('prev', 'left', 'arrow', '‹' ),
16205 fc_button('next', 'right', 'arrow', '›' ),
16206 { tag: 'span', cls: 'fc-header-space' },
16207 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
16215 cls : 'fc-header-center',
16219 cls: 'fc-header-title',
16222 html : 'month / year'
16230 cls : 'fc-header-right',
16232 /* fc_button('month', 'left', '', 'month' ),
16233 fc_button('week', '', '', 'week' ),
16234 fc_button('day', 'right', '', 'day' )
16246 header = this.header;
16249 var cal_heads = function() {
16251 // fixme - handle this.
16253 for (var i =0; i < Date.dayNames.length; i++) {
16254 var d = Date.dayNames[i];
16257 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16258 html : d.substring(0,3)
16262 ret[0].cls += ' fc-first';
16263 ret[6].cls += ' fc-last';
16266 var cal_cell = function(n) {
16269 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16274 cls: 'fc-day-number',
16278 cls: 'fc-day-content',
16282 style: 'position: relative;' // height: 17px;
16294 var cal_rows = function() {
16297 for (var r = 0; r < 6; r++) {
16304 for (var i =0; i < Date.dayNames.length; i++) {
16305 var d = Date.dayNames[i];
16306 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16309 row.cn[0].cls+=' fc-first';
16310 row.cn[0].cn[0].style = 'min-height:90px';
16311 row.cn[6].cls+=' fc-last';
16315 ret[0].cls += ' fc-first';
16316 ret[4].cls += ' fc-prev-last';
16317 ret[5].cls += ' fc-last';
16324 cls: 'fc-border-separate',
16325 style : 'width:100%',
16333 cls : 'fc-first fc-last',
16351 cls : 'fc-content',
16352 style : "position: relative;",
16355 cls : 'fc-view fc-view-month fc-grid',
16356 style : 'position: relative',
16357 unselectable : 'on',
16360 cls : 'fc-event-container',
16361 style : 'position:absolute;z-index:8;top:0;left:0;'
16379 initEvents : function()
16382 throw "can not find store for calendar";
16388 style: "text-align:center",
16392 style: "background-color:white;width:50%;margin:250 auto",
16396 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
16407 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16409 var size = this.el.select('.fc-content', true).first().getSize();
16410 this.maskEl.setSize(size.width, size.height);
16411 this.maskEl.enableDisplayMode("block");
16412 if(!this.loadMask){
16413 this.maskEl.hide();
16416 this.store = Roo.factory(this.store, Roo.data);
16417 this.store.on('load', this.onLoad, this);
16418 this.store.on('beforeload', this.onBeforeLoad, this);
16422 this.cells = this.el.select('.fc-day',true);
16423 //Roo.log(this.cells);
16424 this.textNodes = this.el.query('.fc-day-number');
16425 this.cells.addClassOnOver('fc-state-hover');
16427 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16428 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16429 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16430 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16432 this.on('monthchange', this.onMonthChange, this);
16434 this.update(new Date().clearTime());
16437 resize : function() {
16438 var sz = this.el.getSize();
16440 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16441 this.el.select('.fc-day-content div',true).setHeight(34);
16446 showPrevMonth : function(e){
16447 this.update(this.activeDate.add("mo", -1));
16449 showToday : function(e){
16450 this.update(new Date().clearTime());
16453 showNextMonth : function(e){
16454 this.update(this.activeDate.add("mo", 1));
16458 showPrevYear : function(){
16459 this.update(this.activeDate.add("y", -1));
16463 showNextYear : function(){
16464 this.update(this.activeDate.add("y", 1));
16469 update : function(date)
16471 var vd = this.activeDate;
16472 this.activeDate = date;
16473 // if(vd && this.el){
16474 // var t = date.getTime();
16475 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16476 // Roo.log('using add remove');
16478 // this.fireEvent('monthchange', this, date);
16480 // this.cells.removeClass("fc-state-highlight");
16481 // this.cells.each(function(c){
16482 // if(c.dateValue == t){
16483 // c.addClass("fc-state-highlight");
16484 // setTimeout(function(){
16485 // try{c.dom.firstChild.focus();}catch(e){}
16495 var days = date.getDaysInMonth();
16497 var firstOfMonth = date.getFirstDateOfMonth();
16498 var startingPos = firstOfMonth.getDay()-this.startDay;
16500 if(startingPos < this.startDay){
16504 var pm = date.add(Date.MONTH, -1);
16505 var prevStart = pm.getDaysInMonth()-startingPos;
16507 this.cells = this.el.select('.fc-day',true);
16508 this.textNodes = this.el.query('.fc-day-number');
16509 this.cells.addClassOnOver('fc-state-hover');
16511 var cells = this.cells.elements;
16512 var textEls = this.textNodes;
16514 Roo.each(cells, function(cell){
16515 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16518 days += startingPos;
16520 // convert everything to numbers so it's fast
16521 var day = 86400000;
16522 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16525 //Roo.log(prevStart);
16527 var today = new Date().clearTime().getTime();
16528 var sel = date.clearTime().getTime();
16529 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16530 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16531 var ddMatch = this.disabledDatesRE;
16532 var ddText = this.disabledDatesText;
16533 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16534 var ddaysText = this.disabledDaysText;
16535 var format = this.format;
16537 var setCellClass = function(cal, cell){
16541 //Roo.log('set Cell Class');
16543 var t = d.getTime();
16547 cell.dateValue = t;
16549 cell.className += " fc-today";
16550 cell.className += " fc-state-highlight";
16551 cell.title = cal.todayText;
16554 // disable highlight in other month..
16555 //cell.className += " fc-state-highlight";
16560 cell.className = " fc-state-disabled";
16561 cell.title = cal.minText;
16565 cell.className = " fc-state-disabled";
16566 cell.title = cal.maxText;
16570 if(ddays.indexOf(d.getDay()) != -1){
16571 cell.title = ddaysText;
16572 cell.className = " fc-state-disabled";
16575 if(ddMatch && format){
16576 var fvalue = d.dateFormat(format);
16577 if(ddMatch.test(fvalue)){
16578 cell.title = ddText.replace("%0", fvalue);
16579 cell.className = " fc-state-disabled";
16583 if (!cell.initialClassName) {
16584 cell.initialClassName = cell.dom.className;
16587 cell.dom.className = cell.initialClassName + ' ' + cell.className;
16592 for(; i < startingPos; i++) {
16593 textEls[i].innerHTML = (++prevStart);
16594 d.setDate(d.getDate()+1);
16596 cells[i].className = "fc-past fc-other-month";
16597 setCellClass(this, cells[i]);
16602 for(; i < days; i++){
16603 intDay = i - startingPos + 1;
16604 textEls[i].innerHTML = (intDay);
16605 d.setDate(d.getDate()+1);
16607 cells[i].className = ''; // "x-date-active";
16608 setCellClass(this, cells[i]);
16612 for(; i < 42; i++) {
16613 textEls[i].innerHTML = (++extraDays);
16614 d.setDate(d.getDate()+1);
16616 cells[i].className = "fc-future fc-other-month";
16617 setCellClass(this, cells[i]);
16620 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16622 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16624 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16625 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16627 if(totalRows != 6){
16628 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16629 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16632 this.fireEvent('monthchange', this, date);
16636 if(!this.internalRender){
16637 var main = this.el.dom.firstChild;
16638 var w = main.offsetWidth;
16639 this.el.setWidth(w + this.el.getBorderWidth("lr"));
16640 Roo.fly(main).setWidth(w);
16641 this.internalRender = true;
16642 // opera does not respect the auto grow header center column
16643 // then, after it gets a width opera refuses to recalculate
16644 // without a second pass
16645 if(Roo.isOpera && !this.secondPass){
16646 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16647 this.secondPass = true;
16648 this.update.defer(10, this, [date]);
16655 findCell : function(dt) {
16656 dt = dt.clearTime().getTime();
16658 this.cells.each(function(c){
16659 //Roo.log("check " +c.dateValue + '?=' + dt);
16660 if(c.dateValue == dt){
16670 findCells : function(ev) {
16671 var s = ev.start.clone().clearTime().getTime();
16673 var e= ev.end.clone().clearTime().getTime();
16676 this.cells.each(function(c){
16677 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16679 if(c.dateValue > e){
16682 if(c.dateValue < s){
16691 // findBestRow: function(cells)
16695 // for (var i =0 ; i < cells.length;i++) {
16696 // ret = Math.max(cells[i].rows || 0,ret);
16703 addItem : function(ev)
16705 // look for vertical location slot in
16706 var cells = this.findCells(ev);
16708 // ev.row = this.findBestRow(cells);
16710 // work out the location.
16714 for(var i =0; i < cells.length; i++) {
16716 cells[i].row = cells[0].row;
16719 cells[i].row = cells[i].row + 1;
16729 if (crow.start.getY() == cells[i].getY()) {
16731 crow.end = cells[i];
16748 cells[0].events.push(ev);
16750 this.calevents.push(ev);
16753 clearEvents: function() {
16755 if(!this.calevents){
16759 Roo.each(this.cells.elements, function(c){
16765 Roo.each(this.calevents, function(e) {
16766 Roo.each(e.els, function(el) {
16767 el.un('mouseenter' ,this.onEventEnter, this);
16768 el.un('mouseleave' ,this.onEventLeave, this);
16773 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16779 renderEvents: function()
16783 this.cells.each(function(c) {
16792 if(c.row != c.events.length){
16793 r = 4 - (4 - (c.row - c.events.length));
16796 c.events = ev.slice(0, r);
16797 c.more = ev.slice(r);
16799 if(c.more.length && c.more.length == 1){
16800 c.events.push(c.more.pop());
16803 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16807 this.cells.each(function(c) {
16809 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16812 for (var e = 0; e < c.events.length; e++){
16813 var ev = c.events[e];
16814 var rows = ev.rows;
16816 for(var i = 0; i < rows.length; i++) {
16818 // how many rows should it span..
16821 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16822 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16824 unselectable : "on",
16827 cls: 'fc-event-inner',
16831 // cls: 'fc-event-time',
16832 // html : cells.length > 1 ? '' : ev.time
16836 cls: 'fc-event-title',
16837 html : String.format('{0}', ev.title)
16844 cls: 'ui-resizable-handle ui-resizable-e',
16845 html : '  '
16852 cfg.cls += ' fc-event-start';
16854 if ((i+1) == rows.length) {
16855 cfg.cls += ' fc-event-end';
16858 var ctr = _this.el.select('.fc-event-container',true).first();
16859 var cg = ctr.createChild(cfg);
16861 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16862 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16864 var r = (c.more.length) ? 1 : 0;
16865 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
16866 cg.setWidth(ebox.right - sbox.x -2);
16868 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16869 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16870 cg.on('click', _this.onEventClick, _this, ev);
16881 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16882 style : 'position: absolute',
16883 unselectable : "on",
16886 cls: 'fc-event-inner',
16890 cls: 'fc-event-title',
16898 cls: 'ui-resizable-handle ui-resizable-e',
16899 html : '  '
16905 var ctr = _this.el.select('.fc-event-container',true).first();
16906 var cg = ctr.createChild(cfg);
16908 var sbox = c.select('.fc-day-content',true).first().getBox();
16909 var ebox = c.select('.fc-day-content',true).first().getBox();
16911 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
16912 cg.setWidth(ebox.right - sbox.x -2);
16914 cg.on('click', _this.onMoreEventClick, _this, c.more);
16924 onEventEnter: function (e, el,event,d) {
16925 this.fireEvent('evententer', this, el, event);
16928 onEventLeave: function (e, el,event,d) {
16929 this.fireEvent('eventleave', this, el, event);
16932 onEventClick: function (e, el,event,d) {
16933 this.fireEvent('eventclick', this, el, event);
16936 onMonthChange: function () {
16940 onMoreEventClick: function(e, el, more)
16944 this.calpopover.placement = 'right';
16945 this.calpopover.setTitle('More');
16947 this.calpopover.setContent('');
16949 var ctr = this.calpopover.el.select('.popover-content', true).first();
16951 Roo.each(more, function(m){
16953 cls : 'fc-event-hori fc-event-draggable',
16956 var cg = ctr.createChild(cfg);
16958 cg.on('click', _this.onEventClick, _this, m);
16961 this.calpopover.show(el);
16966 onLoad: function ()
16968 this.calevents = [];
16971 if(this.store.getCount() > 0){
16972 this.store.data.each(function(d){
16975 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
16976 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
16977 time : d.data.start_time,
16978 title : d.data.title,
16979 description : d.data.description,
16980 venue : d.data.venue
16985 this.renderEvents();
16987 if(this.calevents.length && this.loadMask){
16988 this.maskEl.hide();
16992 onBeforeLoad: function()
16994 this.clearEvents();
16996 this.maskEl.show();
17010 * @class Roo.bootstrap.Popover
17011 * @extends Roo.bootstrap.Component
17012 * Bootstrap Popover class
17013 * @cfg {String} html contents of the popover (or false to use children..)
17014 * @cfg {String} title of popover (or false to hide)
17015 * @cfg {String} placement how it is placed
17016 * @cfg {String} trigger click || hover (or false to trigger manually)
17017 * @cfg {String} over what (parent or false to trigger manually.)
17018 * @cfg {Number} delay - delay before showing
17021 * Create a new Popover
17022 * @param {Object} config The config object
17025 Roo.bootstrap.Popover = function(config){
17026 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17032 * After the popover show
17034 * @param {Roo.bootstrap.Popover} this
17039 * After the popover hide
17041 * @param {Roo.bootstrap.Popover} this
17047 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
17049 title: 'Fill in a title',
17052 placement : 'right',
17053 trigger : 'hover', // hover
17059 can_build_overlaid : false,
17061 getChildContainer : function()
17063 return this.el.select('.popover-content',true).first();
17066 getAutoCreate : function(){
17069 cls : 'popover roo-dynamic',
17070 style: 'display:block',
17076 cls : 'popover-inner',
17080 cls: 'popover-title',
17084 cls : 'popover-content',
17095 setTitle: function(str)
17098 this.el.select('.popover-title',true).first().dom.innerHTML = str;
17100 setContent: function(str)
17103 this.el.select('.popover-content',true).first().dom.innerHTML = str;
17105 // as it get's added to the bottom of the page.
17106 onRender : function(ct, position)
17108 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17110 var cfg = Roo.apply({}, this.getAutoCreate());
17114 cfg.cls += ' ' + this.cls;
17117 cfg.style = this.style;
17119 //Roo.log("adding to ");
17120 this.el = Roo.get(document.body).createChild(cfg, position);
17121 // Roo.log(this.el);
17126 initEvents : function()
17128 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17129 this.el.enableDisplayMode('block');
17131 if (this.over === false) {
17134 if (this.triggers === false) {
17137 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17138 var triggers = this.trigger ? this.trigger.split(' ') : [];
17139 Roo.each(triggers, function(trigger) {
17141 if (trigger == 'click') {
17142 on_el.on('click', this.toggle, this);
17143 } else if (trigger != 'manual') {
17144 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
17145 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17147 on_el.on(eventIn ,this.enter, this);
17148 on_el.on(eventOut, this.leave, this);
17159 toggle : function () {
17160 this.hoverState == 'in' ? this.leave() : this.enter();
17163 enter : function () {
17165 clearTimeout(this.timeout);
17167 this.hoverState = 'in';
17169 if (!this.delay || !this.delay.show) {
17174 this.timeout = setTimeout(function () {
17175 if (_t.hoverState == 'in') {
17178 }, this.delay.show)
17181 leave : function() {
17182 clearTimeout(this.timeout);
17184 this.hoverState = 'out';
17186 if (!this.delay || !this.delay.hide) {
17191 this.timeout = setTimeout(function () {
17192 if (_t.hoverState == 'out') {
17195 }, this.delay.hide)
17198 show : function (on_el)
17201 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17205 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17206 if (this.html !== false) {
17207 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17209 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17210 if (!this.title.length) {
17211 this.el.select('.popover-title',true).hide();
17214 var placement = typeof this.placement == 'function' ?
17215 this.placement.call(this, this.el, on_el) :
17218 var autoToken = /\s?auto?\s?/i;
17219 var autoPlace = autoToken.test(placement);
17221 placement = placement.replace(autoToken, '') || 'top';
17225 //this.el.setXY([0,0]);
17227 this.el.dom.style.display='block';
17228 this.el.addClass(placement);
17230 //this.el.appendTo(on_el);
17232 var p = this.getPosition();
17233 var box = this.el.getBox();
17238 var align = Roo.bootstrap.Popover.alignment[placement];
17239 this.el.alignTo(on_el, align[0],align[1]);
17240 //var arrow = this.el.select('.arrow',true).first();
17241 //arrow.set(align[2],
17243 this.el.addClass('in');
17246 if (this.el.hasClass('fade')) {
17250 this.hoverState = 'in';
17252 this.fireEvent('show', this);
17257 this.el.setXY([0,0]);
17258 this.el.removeClass('in');
17260 this.hoverState = null;
17262 this.fireEvent('hide', this);
17267 Roo.bootstrap.Popover.alignment = {
17268 'left' : ['r-l', [-10,0], 'right'],
17269 'right' : ['l-r', [10,0], 'left'],
17270 'bottom' : ['t-b', [0,10], 'top'],
17271 'top' : [ 'b-t', [0,-10], 'bottom']
17282 * @class Roo.bootstrap.Progress
17283 * @extends Roo.bootstrap.Component
17284 * Bootstrap Progress class
17285 * @cfg {Boolean} striped striped of the progress bar
17286 * @cfg {Boolean} active animated of the progress bar
17290 * Create a new Progress
17291 * @param {Object} config The config object
17294 Roo.bootstrap.Progress = function(config){
17295 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17298 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
17303 getAutoCreate : function(){
17311 cfg.cls += ' progress-striped';
17315 cfg.cls += ' active';
17334 * @class Roo.bootstrap.ProgressBar
17335 * @extends Roo.bootstrap.Component
17336 * Bootstrap ProgressBar class
17337 * @cfg {Number} aria_valuenow aria-value now
17338 * @cfg {Number} aria_valuemin aria-value min
17339 * @cfg {Number} aria_valuemax aria-value max
17340 * @cfg {String} label label for the progress bar
17341 * @cfg {String} panel (success | info | warning | danger )
17342 * @cfg {String} role role of the progress bar
17343 * @cfg {String} sr_only text
17347 * Create a new ProgressBar
17348 * @param {Object} config The config object
17351 Roo.bootstrap.ProgressBar = function(config){
17352 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17355 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
17359 aria_valuemax : 100,
17365 getAutoCreate : function()
17370 cls: 'progress-bar',
17371 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17383 cfg.role = this.role;
17386 if(this.aria_valuenow){
17387 cfg['aria-valuenow'] = this.aria_valuenow;
17390 if(this.aria_valuemin){
17391 cfg['aria-valuemin'] = this.aria_valuemin;
17394 if(this.aria_valuemax){
17395 cfg['aria-valuemax'] = this.aria_valuemax;
17398 if(this.label && !this.sr_only){
17399 cfg.html = this.label;
17403 cfg.cls += ' progress-bar-' + this.panel;
17409 update : function(aria_valuenow)
17411 this.aria_valuenow = aria_valuenow;
17413 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17428 * @class Roo.bootstrap.TabGroup
17429 * @extends Roo.bootstrap.Column
17430 * Bootstrap Column class
17431 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17432 * @cfg {Boolean} carousel true to make the group behave like a carousel
17433 * @cfg {Boolean} bullets show bullets for the panels
17434 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17435 * @cfg {Number} timer auto slide timer .. default 0 millisecond
17436 * @cfg {Boolean} showarrow (true|false) show arrow default true
17439 * Create a new TabGroup
17440 * @param {Object} config The config object
17443 Roo.bootstrap.TabGroup = function(config){
17444 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17446 this.navId = Roo.id();
17449 Roo.bootstrap.TabGroup.register(this);
17453 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
17456 transition : false,
17461 slideOnTouch : false,
17464 getAutoCreate : function()
17466 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17468 cfg.cls += ' tab-content';
17470 if (this.carousel) {
17471 cfg.cls += ' carousel slide';
17474 cls : 'carousel-inner',
17478 if(this.bullets && !Roo.isTouch){
17481 cls : 'carousel-bullets',
17485 if(this.bullets_cls){
17486 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17493 cfg.cn[0].cn.push(bullets);
17496 if(this.showarrow){
17497 cfg.cn[0].cn.push({
17499 class : 'carousel-arrow',
17503 class : 'carousel-prev',
17507 class : 'fa fa-chevron-left'
17513 class : 'carousel-next',
17517 class : 'fa fa-chevron-right'
17530 initEvents: function()
17532 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17533 // this.el.on("touchstart", this.onTouchStart, this);
17536 if(this.autoslide){
17539 this.slideFn = window.setInterval(function() {
17540 _this.showPanelNext();
17544 if(this.showarrow){
17545 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17546 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17552 // onTouchStart : function(e, el, o)
17554 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17558 // this.showPanelNext();
17562 getChildContainer : function()
17564 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17568 * register a Navigation item
17569 * @param {Roo.bootstrap.NavItem} the navitem to add
17571 register : function(item)
17573 this.tabs.push( item);
17574 item.navId = this.navId; // not really needed..
17579 getActivePanel : function()
17582 Roo.each(this.tabs, function(t) {
17592 getPanelByName : function(n)
17595 Roo.each(this.tabs, function(t) {
17596 if (t.tabId == n) {
17604 indexOfPanel : function(p)
17607 Roo.each(this.tabs, function(t,i) {
17608 if (t.tabId == p.tabId) {
17617 * show a specific panel
17618 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17619 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17621 showPanel : function (pan)
17623 if(this.transition || typeof(pan) == 'undefined'){
17624 Roo.log("waiting for the transitionend");
17628 if (typeof(pan) == 'number') {
17629 pan = this.tabs[pan];
17632 if (typeof(pan) == 'string') {
17633 pan = this.getPanelByName(pan);
17636 var cur = this.getActivePanel();
17639 Roo.log('pan or acitve pan is undefined');
17643 if (pan.tabId == this.getActivePanel().tabId) {
17647 if (false === cur.fireEvent('beforedeactivate')) {
17651 if(this.bullets > 0 && !Roo.isTouch){
17652 this.setActiveBullet(this.indexOfPanel(pan));
17655 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17657 this.transition = true;
17658 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
17659 var lr = dir == 'next' ? 'left' : 'right';
17660 pan.el.addClass(dir); // or prev
17661 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17662 cur.el.addClass(lr); // or right
17663 pan.el.addClass(lr);
17666 cur.el.on('transitionend', function() {
17667 Roo.log("trans end?");
17669 pan.el.removeClass([lr,dir]);
17670 pan.setActive(true);
17672 cur.el.removeClass([lr]);
17673 cur.setActive(false);
17675 _this.transition = false;
17677 }, this, { single: true } );
17682 cur.setActive(false);
17683 pan.setActive(true);
17688 showPanelNext : function()
17690 var i = this.indexOfPanel(this.getActivePanel());
17692 if (i >= this.tabs.length - 1 && !this.autoslide) {
17696 if (i >= this.tabs.length - 1 && this.autoslide) {
17700 this.showPanel(this.tabs[i+1]);
17703 showPanelPrev : function()
17705 var i = this.indexOfPanel(this.getActivePanel());
17707 if (i < 1 && !this.autoslide) {
17711 if (i < 1 && this.autoslide) {
17712 i = this.tabs.length;
17715 this.showPanel(this.tabs[i-1]);
17719 addBullet: function()
17721 if(!this.bullets || Roo.isTouch){
17724 var ctr = this.el.select('.carousel-bullets',true).first();
17725 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
17726 var bullet = ctr.createChild({
17727 cls : 'bullet bullet-' + i
17728 },ctr.dom.lastChild);
17733 bullet.on('click', (function(e, el, o, ii, t){
17735 e.preventDefault();
17737 this.showPanel(ii);
17739 if(this.autoslide && this.slideFn){
17740 clearInterval(this.slideFn);
17741 this.slideFn = window.setInterval(function() {
17742 _this.showPanelNext();
17746 }).createDelegate(this, [i, bullet], true));
17751 setActiveBullet : function(i)
17757 Roo.each(this.el.select('.bullet', true).elements, function(el){
17758 el.removeClass('selected');
17761 var bullet = this.el.select('.bullet-' + i, true).first();
17767 bullet.addClass('selected');
17778 Roo.apply(Roo.bootstrap.TabGroup, {
17782 * register a Navigation Group
17783 * @param {Roo.bootstrap.NavGroup} the navgroup to add
17785 register : function(navgrp)
17787 this.groups[navgrp.navId] = navgrp;
17791 * fetch a Navigation Group based on the navigation ID
17792 * if one does not exist , it will get created.
17793 * @param {string} the navgroup to add
17794 * @returns {Roo.bootstrap.NavGroup} the navgroup
17796 get: function(navId) {
17797 if (typeof(this.groups[navId]) == 'undefined') {
17798 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17800 return this.groups[navId] ;
17815 * @class Roo.bootstrap.TabPanel
17816 * @extends Roo.bootstrap.Component
17817 * Bootstrap TabPanel class
17818 * @cfg {Boolean} active panel active
17819 * @cfg {String} html panel content
17820 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17821 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17822 * @cfg {String} href click to link..
17826 * Create a new TabPanel
17827 * @param {Object} config The config object
17830 Roo.bootstrap.TabPanel = function(config){
17831 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17835 * Fires when the active status changes
17836 * @param {Roo.bootstrap.TabPanel} this
17837 * @param {Boolean} state the new state
17842 * @event beforedeactivate
17843 * Fires before a tab is de-activated - can be used to do validation on a form.
17844 * @param {Roo.bootstrap.TabPanel} this
17845 * @return {Boolean} false if there is an error
17848 'beforedeactivate': true
17851 this.tabId = this.tabId || Roo.id();
17855 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
17863 getAutoCreate : function(){
17866 // item is needed for carousel - not sure if it has any effect otherwise
17867 cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17868 html: this.html || ''
17872 cfg.cls += ' active';
17876 cfg.tabId = this.tabId;
17883 initEvents: function()
17885 var p = this.parent();
17887 this.navId = this.navId || p.navId;
17889 if (typeof(this.navId) != 'undefined') {
17890 // not really needed.. but just in case.. parent should be a NavGroup.
17891 var tg = Roo.bootstrap.TabGroup.get(this.navId);
17895 var i = tg.tabs.length - 1;
17897 if(this.active && tg.bullets > 0 && i < tg.bullets){
17898 tg.setActiveBullet(i);
17902 this.el.on('click', this.onClick, this);
17905 this.el.on("touchstart", this.onTouchStart, this);
17906 this.el.on("touchmove", this.onTouchMove, this);
17907 this.el.on("touchend", this.onTouchEnd, this);
17912 onRender : function(ct, position)
17914 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
17917 setActive : function(state)
17919 Roo.log("panel - set active " + this.tabId + "=" + state);
17921 this.active = state;
17923 this.el.removeClass('active');
17925 } else if (!this.el.hasClass('active')) {
17926 this.el.addClass('active');
17929 this.fireEvent('changed', this, state);
17932 onClick : function(e)
17934 e.preventDefault();
17936 if(!this.href.length){
17940 window.location.href = this.href;
17949 onTouchStart : function(e)
17951 this.swiping = false;
17953 this.startX = e.browserEvent.touches[0].clientX;
17954 this.startY = e.browserEvent.touches[0].clientY;
17957 onTouchMove : function(e)
17959 this.swiping = true;
17961 this.endX = e.browserEvent.touches[0].clientX;
17962 this.endY = e.browserEvent.touches[0].clientY;
17965 onTouchEnd : function(e)
17972 var tabGroup = this.parent();
17974 if(this.endX > this.startX){ // swiping right
17975 tabGroup.showPanelPrev();
17979 if(this.startX > this.endX){ // swiping left
17980 tabGroup.showPanelNext();
17999 * @class Roo.bootstrap.DateField
18000 * @extends Roo.bootstrap.Input
18001 * Bootstrap DateField class
18002 * @cfg {Number} weekStart default 0
18003 * @cfg {String} viewMode default empty, (months|years)
18004 * @cfg {String} minViewMode default empty, (months|years)
18005 * @cfg {Number} startDate default -Infinity
18006 * @cfg {Number} endDate default Infinity
18007 * @cfg {Boolean} todayHighlight default false
18008 * @cfg {Boolean} todayBtn default false
18009 * @cfg {Boolean} calendarWeeks default false
18010 * @cfg {Object} daysOfWeekDisabled default empty
18011 * @cfg {Boolean} singleMode default false (true | false)
18013 * @cfg {Boolean} keyboardNavigation default true
18014 * @cfg {String} language default en
18017 * Create a new DateField
18018 * @param {Object} config The config object
18021 Roo.bootstrap.DateField = function(config){
18022 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18026 * Fires when this field show.
18027 * @param {Roo.bootstrap.DateField} this
18028 * @param {Mixed} date The date value
18033 * Fires when this field hide.
18034 * @param {Roo.bootstrap.DateField} this
18035 * @param {Mixed} date The date value
18040 * Fires when select a date.
18041 * @param {Roo.bootstrap.DateField} this
18042 * @param {Mixed} date The date value
18046 * @event beforeselect
18047 * Fires when before select a date.
18048 * @param {Roo.bootstrap.DateField} this
18049 * @param {Mixed} date The date value
18051 beforeselect : true
18055 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
18058 * @cfg {String} format
18059 * The default date format string which can be overriden for localization support. The format must be
18060 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18064 * @cfg {String} altFormats
18065 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18066 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18068 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18076 todayHighlight : false,
18082 keyboardNavigation: true,
18084 calendarWeeks: false,
18086 startDate: -Infinity,
18090 daysOfWeekDisabled: [],
18094 singleMode : false,
18096 UTCDate: function()
18098 return new Date(Date.UTC.apply(Date, arguments));
18101 UTCToday: function()
18103 var today = new Date();
18104 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18107 getDate: function() {
18108 var d = this.getUTCDate();
18109 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18112 getUTCDate: function() {
18116 setDate: function(d) {
18117 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18120 setUTCDate: function(d) {
18122 this.setValue(this.formatDate(this.date));
18125 onRender: function(ct, position)
18128 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18130 this.language = this.language || 'en';
18131 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18132 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18134 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18135 this.format = this.format || 'm/d/y';
18136 this.isInline = false;
18137 this.isInput = true;
18138 this.component = this.el.select('.add-on', true).first() || false;
18139 this.component = (this.component && this.component.length === 0) ? false : this.component;
18140 this.hasInput = this.component && this.inputEl().length;
18142 if (typeof(this.minViewMode === 'string')) {
18143 switch (this.minViewMode) {
18145 this.minViewMode = 1;
18148 this.minViewMode = 2;
18151 this.minViewMode = 0;
18156 if (typeof(this.viewMode === 'string')) {
18157 switch (this.viewMode) {
18170 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18172 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18174 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18176 this.picker().on('mousedown', this.onMousedown, this);
18177 this.picker().on('click', this.onClick, this);
18179 this.picker().addClass('datepicker-dropdown');
18181 this.startViewMode = this.viewMode;
18183 if(this.singleMode){
18184 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18185 v.setVisibilityMode(Roo.Element.DISPLAY);
18189 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18190 v.setStyle('width', '189px');
18194 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18195 if(!this.calendarWeeks){
18200 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18201 v.attr('colspan', function(i, val){
18202 return parseInt(val) + 1;
18207 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18209 this.setStartDate(this.startDate);
18210 this.setEndDate(this.endDate);
18212 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18219 if(this.isInline) {
18224 picker : function()
18226 return this.pickerEl;
18227 // return this.el.select('.datepicker', true).first();
18230 fillDow: function()
18232 var dowCnt = this.weekStart;
18241 if(this.calendarWeeks){
18249 while (dowCnt < this.weekStart + 7) {
18253 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18257 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18260 fillMonths: function()
18263 var months = this.picker().select('>.datepicker-months td', true).first();
18265 months.dom.innerHTML = '';
18271 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18274 months.createChild(month);
18281 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;
18283 if (this.date < this.startDate) {
18284 this.viewDate = new Date(this.startDate);
18285 } else if (this.date > this.endDate) {
18286 this.viewDate = new Date(this.endDate);
18288 this.viewDate = new Date(this.date);
18296 var d = new Date(this.viewDate),
18297 year = d.getUTCFullYear(),
18298 month = d.getUTCMonth(),
18299 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18300 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18301 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18302 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18303 currentDate = this.date && this.date.valueOf(),
18304 today = this.UTCToday();
18306 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18308 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18310 // this.picker.select('>tfoot th.today').
18311 // .text(dates[this.language].today)
18312 // .toggle(this.todayBtn !== false);
18314 this.updateNavArrows();
18317 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18319 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18321 prevMonth.setUTCDate(day);
18323 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18325 var nextMonth = new Date(prevMonth);
18327 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18329 nextMonth = nextMonth.valueOf();
18331 var fillMonths = false;
18333 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18335 while(prevMonth.valueOf() < nextMonth) {
18338 if (prevMonth.getUTCDay() === this.weekStart) {
18340 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18348 if(this.calendarWeeks){
18349 // ISO 8601: First week contains first thursday.
18350 // ISO also states week starts on Monday, but we can be more abstract here.
18352 // Start of current week: based on weekstart/current date
18353 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18354 // Thursday of this week
18355 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18356 // First Thursday of year, year from thursday
18357 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18358 // Calendar week: ms between thursdays, div ms per day, div 7 days
18359 calWeek = (th - yth) / 864e5 / 7 + 1;
18361 fillMonths.cn.push({
18369 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18371 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18374 if (this.todayHighlight &&
18375 prevMonth.getUTCFullYear() == today.getFullYear() &&
18376 prevMonth.getUTCMonth() == today.getMonth() &&
18377 prevMonth.getUTCDate() == today.getDate()) {
18378 clsName += ' today';
18381 if (currentDate && prevMonth.valueOf() === currentDate) {
18382 clsName += ' active';
18385 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18386 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18387 clsName += ' disabled';
18390 fillMonths.cn.push({
18392 cls: 'day ' + clsName,
18393 html: prevMonth.getDate()
18396 prevMonth.setDate(prevMonth.getDate()+1);
18399 var currentYear = this.date && this.date.getUTCFullYear();
18400 var currentMonth = this.date && this.date.getUTCMonth();
18402 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18404 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18405 v.removeClass('active');
18407 if(currentYear === year && k === currentMonth){
18408 v.addClass('active');
18411 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18412 v.addClass('disabled');
18418 year = parseInt(year/10, 10) * 10;
18420 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18422 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18425 for (var i = -1; i < 11; i++) {
18426 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18428 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18436 showMode: function(dir)
18439 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18442 Roo.each(this.picker().select('>div',true).elements, function(v){
18443 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18446 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18451 if(this.isInline) {
18455 this.picker().removeClass(['bottom', 'top']);
18457 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18459 * place to the top of element!
18463 this.picker().addClass('top');
18464 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18469 this.picker().addClass('bottom');
18471 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18474 parseDate : function(value)
18476 if(!value || value instanceof Date){
18479 var v = Date.parseDate(value, this.format);
18480 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18481 v = Date.parseDate(value, 'Y-m-d');
18483 if(!v && this.altFormats){
18484 if(!this.altFormatsArray){
18485 this.altFormatsArray = this.altFormats.split("|");
18487 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18488 v = Date.parseDate(value, this.altFormatsArray[i]);
18494 formatDate : function(date, fmt)
18496 return (!date || !(date instanceof Date)) ?
18497 date : date.dateFormat(fmt || this.format);
18500 onFocus : function()
18502 Roo.bootstrap.DateField.superclass.onFocus.call(this);
18506 onBlur : function()
18508 Roo.bootstrap.DateField.superclass.onBlur.call(this);
18510 var d = this.inputEl().getValue();
18519 this.picker().show();
18523 this.fireEvent('show', this, this.date);
18528 if(this.isInline) {
18531 this.picker().hide();
18532 this.viewMode = this.startViewMode;
18535 this.fireEvent('hide', this, this.date);
18539 onMousedown: function(e)
18541 e.stopPropagation();
18542 e.preventDefault();
18547 Roo.bootstrap.DateField.superclass.keyup.call(this);
18551 setValue: function(v)
18553 if(this.fireEvent('beforeselect', this, v) !== false){
18554 var d = new Date(this.parseDate(v) ).clearTime();
18556 if(isNaN(d.getTime())){
18557 this.date = this.viewDate = '';
18558 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18562 v = this.formatDate(d);
18564 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18566 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18570 this.fireEvent('select', this, this.date);
18574 getValue: function()
18576 return this.formatDate(this.date);
18579 fireKey: function(e)
18581 if (!this.picker().isVisible()){
18582 if (e.keyCode == 27) { // allow escape to hide and re-show picker
18588 var dateChanged = false,
18590 newDate, newViewDate;
18595 e.preventDefault();
18599 if (!this.keyboardNavigation) {
18602 dir = e.keyCode == 37 ? -1 : 1;
18605 newDate = this.moveYear(this.date, dir);
18606 newViewDate = this.moveYear(this.viewDate, dir);
18607 } else if (e.shiftKey){
18608 newDate = this.moveMonth(this.date, dir);
18609 newViewDate = this.moveMonth(this.viewDate, dir);
18611 newDate = new Date(this.date);
18612 newDate.setUTCDate(this.date.getUTCDate() + dir);
18613 newViewDate = new Date(this.viewDate);
18614 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18616 if (this.dateWithinRange(newDate)){
18617 this.date = newDate;
18618 this.viewDate = newViewDate;
18619 this.setValue(this.formatDate(this.date));
18621 e.preventDefault();
18622 dateChanged = true;
18627 if (!this.keyboardNavigation) {
18630 dir = e.keyCode == 38 ? -1 : 1;
18632 newDate = this.moveYear(this.date, dir);
18633 newViewDate = this.moveYear(this.viewDate, dir);
18634 } else if (e.shiftKey){
18635 newDate = this.moveMonth(this.date, dir);
18636 newViewDate = this.moveMonth(this.viewDate, dir);
18638 newDate = new Date(this.date);
18639 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18640 newViewDate = new Date(this.viewDate);
18641 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18643 if (this.dateWithinRange(newDate)){
18644 this.date = newDate;
18645 this.viewDate = newViewDate;
18646 this.setValue(this.formatDate(this.date));
18648 e.preventDefault();
18649 dateChanged = true;
18653 this.setValue(this.formatDate(this.date));
18655 e.preventDefault();
18658 this.setValue(this.formatDate(this.date));
18672 onClick: function(e)
18674 e.stopPropagation();
18675 e.preventDefault();
18677 var target = e.getTarget();
18679 if(target.nodeName.toLowerCase() === 'i'){
18680 target = Roo.get(target).dom.parentNode;
18683 var nodeName = target.nodeName;
18684 var className = target.className;
18685 var html = target.innerHTML;
18686 //Roo.log(nodeName);
18688 switch(nodeName.toLowerCase()) {
18690 switch(className) {
18696 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18697 switch(this.viewMode){
18699 this.viewDate = this.moveMonth(this.viewDate, dir);
18703 this.viewDate = this.moveYear(this.viewDate, dir);
18709 var date = new Date();
18710 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18712 this.setValue(this.formatDate(this.date));
18719 if (className.indexOf('disabled') < 0) {
18720 this.viewDate.setUTCDate(1);
18721 if (className.indexOf('month') > -1) {
18722 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18724 var year = parseInt(html, 10) || 0;
18725 this.viewDate.setUTCFullYear(year);
18729 if(this.singleMode){
18730 this.setValue(this.formatDate(this.viewDate));
18741 //Roo.log(className);
18742 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
18743 var day = parseInt(html, 10) || 1;
18744 var year = this.viewDate.getUTCFullYear(),
18745 month = this.viewDate.getUTCMonth();
18747 if (className.indexOf('old') > -1) {
18754 } else if (className.indexOf('new') > -1) {
18762 //Roo.log([year,month,day]);
18763 this.date = this.UTCDate(year, month, day,0,0,0,0);
18764 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
18766 //Roo.log(this.formatDate(this.date));
18767 this.setValue(this.formatDate(this.date));
18774 setStartDate: function(startDate)
18776 this.startDate = startDate || -Infinity;
18777 if (this.startDate !== -Infinity) {
18778 this.startDate = this.parseDate(this.startDate);
18781 this.updateNavArrows();
18784 setEndDate: function(endDate)
18786 this.endDate = endDate || Infinity;
18787 if (this.endDate !== Infinity) {
18788 this.endDate = this.parseDate(this.endDate);
18791 this.updateNavArrows();
18794 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18796 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18797 if (typeof(this.daysOfWeekDisabled) !== 'object') {
18798 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18800 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18801 return parseInt(d, 10);
18804 this.updateNavArrows();
18807 updateNavArrows: function()
18809 if(this.singleMode){
18813 var d = new Date(this.viewDate),
18814 year = d.getUTCFullYear(),
18815 month = d.getUTCMonth();
18817 Roo.each(this.picker().select('.prev', true).elements, function(v){
18819 switch (this.viewMode) {
18822 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18828 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18835 Roo.each(this.picker().select('.next', true).elements, function(v){
18837 switch (this.viewMode) {
18840 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18846 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18854 moveMonth: function(date, dir)
18859 var new_date = new Date(date.valueOf()),
18860 day = new_date.getUTCDate(),
18861 month = new_date.getUTCMonth(),
18862 mag = Math.abs(dir),
18864 dir = dir > 0 ? 1 : -1;
18867 // If going back one month, make sure month is not current month
18868 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18870 return new_date.getUTCMonth() == month;
18872 // If going forward one month, make sure month is as expected
18873 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18875 return new_date.getUTCMonth() != new_month;
18877 new_month = month + dir;
18878 new_date.setUTCMonth(new_month);
18879 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18880 if (new_month < 0 || new_month > 11) {
18881 new_month = (new_month + 12) % 12;
18884 // For magnitudes >1, move one month at a time...
18885 for (var i=0; i<mag; i++) {
18886 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18887 new_date = this.moveMonth(new_date, dir);
18889 // ...then reset the day, keeping it in the new month
18890 new_month = new_date.getUTCMonth();
18891 new_date.setUTCDate(day);
18893 return new_month != new_date.getUTCMonth();
18896 // Common date-resetting loop -- if date is beyond end of month, make it
18899 new_date.setUTCDate(--day);
18900 new_date.setUTCMonth(new_month);
18905 moveYear: function(date, dir)
18907 return this.moveMonth(date, dir*12);
18910 dateWithinRange: function(date)
18912 return date >= this.startDate && date <= this.endDate;
18918 this.picker().remove();
18921 validateValue : function(value)
18923 if(value.length < 1) {
18924 if(this.allowBlank){
18930 if(value.length < this.minLength){
18933 if(value.length > this.maxLength){
18937 var vt = Roo.form.VTypes;
18938 if(!vt[this.vtype](value, this)){
18942 if(typeof this.validator == "function"){
18943 var msg = this.validator(value);
18949 if(this.regex && !this.regex.test(value)){
18953 if(typeof(this.parseDate(value)) == 'undefined'){
18957 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
18961 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
18971 Roo.apply(Roo.bootstrap.DateField, {
18982 html: '<i class="fa fa-arrow-left"/>'
18992 html: '<i class="fa fa-arrow-right"/>'
19034 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19035 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19036 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19037 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19038 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19051 navFnc: 'FullYear',
19056 navFnc: 'FullYear',
19061 Roo.apply(Roo.bootstrap.DateField, {
19065 cls: 'datepicker dropdown-menu roo-dynamic',
19069 cls: 'datepicker-days',
19073 cls: 'table-condensed',
19075 Roo.bootstrap.DateField.head,
19079 Roo.bootstrap.DateField.footer
19086 cls: 'datepicker-months',
19090 cls: 'table-condensed',
19092 Roo.bootstrap.DateField.head,
19093 Roo.bootstrap.DateField.content,
19094 Roo.bootstrap.DateField.footer
19101 cls: 'datepicker-years',
19105 cls: 'table-condensed',
19107 Roo.bootstrap.DateField.head,
19108 Roo.bootstrap.DateField.content,
19109 Roo.bootstrap.DateField.footer
19128 * @class Roo.bootstrap.TimeField
19129 * @extends Roo.bootstrap.Input
19130 * Bootstrap DateField class
19134 * Create a new TimeField
19135 * @param {Object} config The config object
19138 Roo.bootstrap.TimeField = function(config){
19139 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19143 * Fires when this field show.
19144 * @param {Roo.bootstrap.DateField} thisthis
19145 * @param {Mixed} date The date value
19150 * Fires when this field hide.
19151 * @param {Roo.bootstrap.DateField} this
19152 * @param {Mixed} date The date value
19157 * Fires when select a date.
19158 * @param {Roo.bootstrap.DateField} this
19159 * @param {Mixed} date The date value
19165 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
19168 * @cfg {String} format
19169 * The default time format string which can be overriden for localization support. The format must be
19170 * valid according to {@link Date#parseDate} (defaults to 'H:i').
19174 onRender: function(ct, position)
19177 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19179 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19181 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19183 this.pop = this.picker().select('>.datepicker-time',true).first();
19184 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19186 this.picker().on('mousedown', this.onMousedown, this);
19187 this.picker().on('click', this.onClick, this);
19189 this.picker().addClass('datepicker-dropdown');
19194 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19195 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19196 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19197 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19198 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19199 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19203 fireKey: function(e){
19204 if (!this.picker().isVisible()){
19205 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19211 e.preventDefault();
19219 this.onTogglePeriod();
19222 this.onIncrementMinutes();
19225 this.onDecrementMinutes();
19234 onClick: function(e) {
19235 e.stopPropagation();
19236 e.preventDefault();
19239 picker : function()
19241 return this.el.select('.datepicker', true).first();
19244 fillTime: function()
19246 var time = this.pop.select('tbody', true).first();
19248 time.dom.innerHTML = '';
19263 cls: 'hours-up glyphicon glyphicon-chevron-up'
19283 cls: 'minutes-up glyphicon glyphicon-chevron-up'
19304 cls: 'timepicker-hour',
19319 cls: 'timepicker-minute',
19334 cls: 'btn btn-primary period',
19356 cls: 'hours-down glyphicon glyphicon-chevron-down'
19376 cls: 'minutes-down glyphicon glyphicon-chevron-down'
19394 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19401 var hours = this.time.getHours();
19402 var minutes = this.time.getMinutes();
19415 hours = hours - 12;
19419 hours = '0' + hours;
19423 minutes = '0' + minutes;
19426 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19427 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19428 this.pop.select('button', true).first().dom.innerHTML = period;
19434 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19436 var cls = ['bottom'];
19438 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19445 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19450 this.picker().addClass(cls.join('-'));
19454 Roo.each(cls, function(c){
19456 _this.picker().setTop(_this.inputEl().getHeight());
19460 _this.picker().setTop(0 - _this.picker().getHeight());
19465 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19469 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19476 onFocus : function()
19478 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19482 onBlur : function()
19484 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19490 this.picker().show();
19495 this.fireEvent('show', this, this.date);
19500 this.picker().hide();
19503 this.fireEvent('hide', this, this.date);
19506 setTime : function()
19509 this.setValue(this.time.format(this.format));
19511 this.fireEvent('select', this, this.date);
19516 onMousedown: function(e){
19517 e.stopPropagation();
19518 e.preventDefault();
19521 onIncrementHours: function()
19523 Roo.log('onIncrementHours');
19524 this.time = this.time.add(Date.HOUR, 1);
19529 onDecrementHours: function()
19531 Roo.log('onDecrementHours');
19532 this.time = this.time.add(Date.HOUR, -1);
19536 onIncrementMinutes: function()
19538 Roo.log('onIncrementMinutes');
19539 this.time = this.time.add(Date.MINUTE, 1);
19543 onDecrementMinutes: function()
19545 Roo.log('onDecrementMinutes');
19546 this.time = this.time.add(Date.MINUTE, -1);
19550 onTogglePeriod: function()
19552 Roo.log('onTogglePeriod');
19553 this.time = this.time.add(Date.HOUR, 12);
19560 Roo.apply(Roo.bootstrap.TimeField, {
19590 cls: 'btn btn-info ok',
19602 Roo.apply(Roo.bootstrap.TimeField, {
19606 cls: 'datepicker dropdown-menu',
19610 cls: 'datepicker-time',
19614 cls: 'table-condensed',
19616 Roo.bootstrap.TimeField.content,
19617 Roo.bootstrap.TimeField.footer
19636 * @class Roo.bootstrap.MonthField
19637 * @extends Roo.bootstrap.Input
19638 * Bootstrap MonthField class
19640 * @cfg {String} language default en
19643 * Create a new MonthField
19644 * @param {Object} config The config object
19647 Roo.bootstrap.MonthField = function(config){
19648 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19653 * Fires when this field show.
19654 * @param {Roo.bootstrap.MonthField} this
19655 * @param {Mixed} date The date value
19660 * Fires when this field hide.
19661 * @param {Roo.bootstrap.MonthField} this
19662 * @param {Mixed} date The date value
19667 * Fires when select a date.
19668 * @param {Roo.bootstrap.MonthField} this
19669 * @param {String} oldvalue The old value
19670 * @param {String} newvalue The new value
19676 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
19678 onRender: function(ct, position)
19681 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19683 this.language = this.language || 'en';
19684 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19685 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19687 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19688 this.isInline = false;
19689 this.isInput = true;
19690 this.component = this.el.select('.add-on', true).first() || false;
19691 this.component = (this.component && this.component.length === 0) ? false : this.component;
19692 this.hasInput = this.component && this.inputEL().length;
19694 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19696 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19698 this.picker().on('mousedown', this.onMousedown, this);
19699 this.picker().on('click', this.onClick, this);
19701 this.picker().addClass('datepicker-dropdown');
19703 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19704 v.setStyle('width', '189px');
19711 if(this.isInline) {
19717 setValue: function(v, suppressEvent)
19719 var o = this.getValue();
19721 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
19725 if(suppressEvent !== true){
19726 this.fireEvent('select', this, o, v);
19731 getValue: function()
19736 onClick: function(e)
19738 e.stopPropagation();
19739 e.preventDefault();
19741 var target = e.getTarget();
19743 if(target.nodeName.toLowerCase() === 'i'){
19744 target = Roo.get(target).dom.parentNode;
19747 var nodeName = target.nodeName;
19748 var className = target.className;
19749 var html = target.innerHTML;
19751 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
19755 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
19757 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19763 picker : function()
19765 return this.pickerEl;
19768 fillMonths: function()
19771 var months = this.picker().select('>.datepicker-months td', true).first();
19773 months.dom.innerHTML = '';
19779 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
19782 months.createChild(month);
19791 if(typeof(this.vIndex) == 'undefined' && this.value.length){
19792 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19795 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19796 e.removeClass('active');
19798 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19799 e.addClass('active');
19806 if(this.isInline) {
19810 this.picker().removeClass(['bottom', 'top']);
19812 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19814 * place to the top of element!
19818 this.picker().addClass('top');
19819 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19824 this.picker().addClass('bottom');
19826 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19829 onFocus : function()
19831 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19835 onBlur : function()
19837 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19839 var d = this.inputEl().getValue();
19848 this.picker().show();
19849 this.picker().select('>.datepicker-months', true).first().show();
19853 this.fireEvent('show', this, this.date);
19858 if(this.isInline) {
19861 this.picker().hide();
19862 this.fireEvent('hide', this, this.date);
19866 onMousedown: function(e)
19868 e.stopPropagation();
19869 e.preventDefault();
19874 Roo.bootstrap.MonthField.superclass.keyup.call(this);
19878 fireKey: function(e)
19880 if (!this.picker().isVisible()){
19881 if (e.keyCode == 27) {// allow escape to hide and re-show picker
19892 e.preventDefault();
19896 dir = e.keyCode == 37 ? -1 : 1;
19898 this.vIndex = this.vIndex + dir;
19900 if(this.vIndex < 0){
19904 if(this.vIndex > 11){
19908 if(isNaN(this.vIndex)){
19912 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19918 dir = e.keyCode == 38 ? -1 : 1;
19920 this.vIndex = this.vIndex + dir * 4;
19922 if(this.vIndex < 0){
19926 if(this.vIndex > 11){
19930 if(isNaN(this.vIndex)){
19934 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19939 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19940 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19944 e.preventDefault();
19947 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19948 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19964 this.picker().remove();
19969 Roo.apply(Roo.bootstrap.MonthField, {
19988 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19989 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
19994 Roo.apply(Roo.bootstrap.MonthField, {
19998 cls: 'datepicker dropdown-menu roo-dynamic',
20002 cls: 'datepicker-months',
20006 cls: 'table-condensed',
20008 Roo.bootstrap.DateField.content
20028 * @class Roo.bootstrap.CheckBox
20029 * @extends Roo.bootstrap.Input
20030 * Bootstrap CheckBox class
20032 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20033 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20034 * @cfg {String} boxLabel The text that appears beside the checkbox
20035 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20036 * @cfg {Boolean} checked initnal the element
20037 * @cfg {Boolean} inline inline the element (default false)
20038 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20041 * Create a new CheckBox
20042 * @param {Object} config The config object
20045 Roo.bootstrap.CheckBox = function(config){
20046 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20051 * Fires when the element is checked or unchecked.
20052 * @param {Roo.bootstrap.CheckBox} this This input
20053 * @param {Boolean} checked The new checked value
20060 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
20062 inputType: 'checkbox',
20070 getAutoCreate : function()
20072 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20078 cfg.cls = 'form-group ' + this.inputType; //input-group
20081 cfg.cls += ' ' + this.inputType + '-inline';
20087 type : this.inputType,
20088 value : this.inputValue,
20089 cls : 'roo-' + this.inputType, //'form-box',
20090 placeholder : this.placeholder || ''
20094 if(this.inputType != 'radio'){
20098 cls : 'roo-hidden-value',
20099 value : this.checked ? this.valueOff : this.inputValue
20104 if (this.weight) { // Validity check?
20105 cfg.cls += " " + this.inputType + "-" + this.weight;
20108 if (this.disabled) {
20109 input.disabled=true;
20113 input.checked = this.checked;
20120 input.name = this.name;
20122 if(this.inputType != 'radio'){
20123 hidden.name = this.name;
20124 input.name = '_hidden_' + this.name;
20129 input.cls += ' input-' + this.size;
20134 ['xs','sm','md','lg'].map(function(size){
20135 if (settings[size]) {
20136 cfg.cls += ' col-' + size + '-' + settings[size];
20140 var inputblock = input;
20142 if (this.before || this.after) {
20145 cls : 'input-group',
20150 inputblock.cn.push({
20152 cls : 'input-group-addon',
20157 inputblock.cn.push(input);
20159 if(this.inputType != 'radio'){
20160 inputblock.cn.push(hidden);
20164 inputblock.cn.push({
20166 cls : 'input-group-addon',
20173 if (align ==='left' && this.fieldLabel.length) {
20174 // Roo.log("left and has label");
20179 cls : 'control-label',
20180 html : this.fieldLabel
20191 if(this.labelWidth > 12){
20192 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20195 if(this.labelWidth < 13 && this.labelmd == 0){
20196 this.labelmd = this.labelWidth;
20199 if(this.labellg > 0){
20200 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20201 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20204 if(this.labelmd > 0){
20205 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20206 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20209 if(this.labelsm > 0){
20210 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20211 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20214 if(this.labelxs > 0){
20215 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20216 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20219 } else if ( this.fieldLabel.length) {
20220 // Roo.log(" label");
20224 tag: this.boxLabel ? 'span' : 'label',
20226 cls: 'control-label box-input-label',
20227 //cls : 'input-group-addon',
20228 html : this.fieldLabel
20238 // Roo.log(" no label && no align");
20239 cfg.cn = [ inputblock ] ;
20245 var boxLabelCfg = {
20247 //'for': id, // box label is handled by onclick - so no for...
20249 html: this.boxLabel
20253 boxLabelCfg.tooltip = this.tooltip;
20256 cfg.cn.push(boxLabelCfg);
20259 if(this.inputType != 'radio'){
20260 cfg.cn.push(hidden);
20268 * return the real input element.
20270 inputEl: function ()
20272 return this.el.select('input.roo-' + this.inputType,true).first();
20274 hiddenEl: function ()
20276 return this.el.select('input.roo-hidden-value',true).first();
20279 labelEl: function()
20281 return this.el.select('label.control-label',true).first();
20283 /* depricated... */
20287 return this.labelEl();
20290 boxLabelEl: function()
20292 return this.el.select('label.box-label',true).first();
20295 initEvents : function()
20297 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20299 this.inputEl().on('click', this.onClick, this);
20301 if (this.boxLabel) {
20302 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
20305 this.startValue = this.getValue();
20308 Roo.bootstrap.CheckBox.register(this);
20312 onClick : function()
20314 this.setChecked(!this.checked);
20317 setChecked : function(state,suppressEvent)
20319 this.startValue = this.getValue();
20321 if(this.inputType == 'radio'){
20323 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20324 e.dom.checked = false;
20327 this.inputEl().dom.checked = true;
20329 this.inputEl().dom.value = this.inputValue;
20331 if(suppressEvent !== true){
20332 this.fireEvent('check', this, true);
20340 this.checked = state;
20342 this.inputEl().dom.checked = state;
20345 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20347 if(suppressEvent !== true){
20348 this.fireEvent('check', this, state);
20354 getValue : function()
20356 if(this.inputType == 'radio'){
20357 return this.getGroupValue();
20360 return this.hiddenEl().dom.value;
20364 getGroupValue : function()
20366 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20370 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20373 setValue : function(v,suppressEvent)
20375 if(this.inputType == 'radio'){
20376 this.setGroupValue(v, suppressEvent);
20380 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20385 setGroupValue : function(v, suppressEvent)
20387 this.startValue = this.getValue();
20389 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20390 e.dom.checked = false;
20392 if(e.dom.value == v){
20393 e.dom.checked = true;
20397 if(suppressEvent !== true){
20398 this.fireEvent('check', this, true);
20406 validate : function()
20410 (this.inputType == 'radio' && this.validateRadio()) ||
20411 (this.inputType == 'checkbox' && this.validateCheckbox())
20417 this.markInvalid();
20421 validateRadio : function()
20423 if(this.allowBlank){
20429 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20430 if(!e.dom.checked){
20442 validateCheckbox : function()
20445 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20446 //return (this.getValue() == this.inputValue) ? true : false;
20449 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20457 for(var i in group){
20462 r = (group[i].getValue() == group[i].inputValue) ? true : false;
20469 * Mark this field as valid
20471 markValid : function()
20475 this.fireEvent('valid', this);
20477 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20480 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20487 if(this.inputType == 'radio'){
20488 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20489 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20490 e.findParent('.form-group', false, true).addClass(_this.validClass);
20497 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20498 this.el.findParent('.form-group', false, true).addClass(this.validClass);
20502 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20508 for(var i in group){
20509 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20510 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20515 * Mark this field as invalid
20516 * @param {String} msg The validation message
20518 markInvalid : function(msg)
20520 if(this.allowBlank){
20526 this.fireEvent('invalid', this, msg);
20528 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20531 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20535 label.markInvalid();
20538 if(this.inputType == 'radio'){
20539 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20540 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20541 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20548 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20549 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20553 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20559 for(var i in group){
20560 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20561 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20566 clearInvalid : function()
20568 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20570 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20572 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20575 label.iconEl.removeClass(label.validClass);
20576 label.iconEl.removeClass(label.invalidClass);
20580 disable : function()
20582 if(this.inputType != 'radio'){
20583 Roo.bootstrap.CheckBox.superclass.disable.call(this);
20590 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20591 _this.getActionEl().addClass(this.disabledClass);
20592 e.dom.disabled = true;
20596 this.disabled = true;
20597 this.fireEvent("disable", this);
20601 enable : function()
20603 if(this.inputType != 'radio'){
20604 Roo.bootstrap.CheckBox.superclass.enable.call(this);
20611 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20612 _this.getActionEl().removeClass(this.disabledClass);
20613 e.dom.disabled = false;
20617 this.disabled = false;
20618 this.fireEvent("enable", this);
20624 Roo.apply(Roo.bootstrap.CheckBox, {
20629 * register a CheckBox Group
20630 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20632 register : function(checkbox)
20634 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20635 this.groups[checkbox.groupId] = {};
20638 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20642 this.groups[checkbox.groupId][checkbox.name] = checkbox;
20646 * fetch a CheckBox Group based on the group ID
20647 * @param {string} the group ID
20648 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20650 get: function(groupId) {
20651 if (typeof(this.groups[groupId]) == 'undefined') {
20655 return this.groups[groupId] ;
20668 * @class Roo.bootstrap.Radio
20669 * @extends Roo.bootstrap.Component
20670 * Bootstrap Radio class
20671 * @cfg {String} boxLabel - the label associated
20672 * @cfg {String} value - the value of radio
20675 * Create a new Radio
20676 * @param {Object} config The config object
20678 Roo.bootstrap.Radio = function(config){
20679 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
20683 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
20689 getAutoCreate : function()
20693 cls : 'form-group radio',
20698 html : this.boxLabel
20706 initEvents : function()
20708 this.parent().register(this);
20710 this.el.on('click', this.onClick, this);
20714 onClick : function()
20716 this.setChecked(true);
20719 setChecked : function(state, suppressEvent)
20721 this.parent().setValue(this.value, suppressEvent);
20736 * @class Roo.bootstrap.SecurePass
20737 * @extends Roo.bootstrap.Input
20738 * Bootstrap SecurePass class
20742 * Create a new SecurePass
20743 * @param {Object} config The config object
20746 Roo.bootstrap.SecurePass = function (config) {
20747 // these go here, so the translation tool can replace them..
20749 PwdEmpty: "Please type a password, and then retype it to confirm.",
20750 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20751 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20752 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20753 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20754 FNInPwd: "Your password can't contain your first name. Please type a different password.",
20755 LNInPwd: "Your password can't contain your last name. Please type a different password.",
20756 TooWeak: "Your password is Too Weak."
20758 this.meterLabel = "Password strength:";
20759 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
20760 this.meterClass = [
20761 "roo-password-meter-tooweak",
20762 "roo-password-meter-weak",
20763 "roo-password-meter-medium",
20764 "roo-password-meter-strong",
20765 "roo-password-meter-grey"
20770 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
20773 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
20775 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
20777 * PwdEmpty: "Please type a password, and then retype it to confirm.",
20778 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20779 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20780 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20781 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20782 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
20783 * LNInPwd: "Your password can't contain your last name. Please type a different password."
20793 * @cfg {String/Object} Label for the strength meter (defaults to
20794 * 'Password strength:')
20799 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
20800 * ['Weak', 'Medium', 'Strong'])
20803 pwdStrengths: false,
20816 initEvents: function ()
20818 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
20820 if (this.el.is('input[type=password]') && Roo.isSafari) {
20821 this.el.on('keydown', this.SafariOnKeyDown, this);
20824 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
20827 onRender: function (ct, position)
20829 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
20830 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
20831 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
20833 this.trigger.createChild({
20838 cls: 'roo-password-meter-grey col-xs-12',
20841 //width: this.meterWidth + 'px'
20845 cls: 'roo-password-meter-text'
20851 if (this.hideTrigger) {
20852 this.trigger.setDisplayed(false);
20854 this.setSize(this.width || '', this.height || '');
20857 onDestroy: function ()
20859 if (this.trigger) {
20860 this.trigger.removeAllListeners();
20861 this.trigger.remove();
20864 this.wrap.remove();
20866 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
20869 checkStrength: function ()
20871 var pwd = this.inputEl().getValue();
20872 if (pwd == this._lastPwd) {
20877 if (this.ClientSideStrongPassword(pwd)) {
20879 } else if (this.ClientSideMediumPassword(pwd)) {
20881 } else if (this.ClientSideWeakPassword(pwd)) {
20887 Roo.log('strength1: ' + strength);
20889 //var pm = this.trigger.child('div/div/div').dom;
20890 var pm = this.trigger.child('div/div');
20891 pm.removeClass(this.meterClass);
20892 pm.addClass(this.meterClass[strength]);
20895 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
20897 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
20899 this._lastPwd = pwd;
20903 Roo.bootstrap.SecurePass.superclass.reset.call(this);
20905 this._lastPwd = '';
20907 var pm = this.trigger.child('div/div');
20908 pm.removeClass(this.meterClass);
20909 pm.addClass('roo-password-meter-grey');
20912 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
20915 this.inputEl().dom.type='password';
20918 validateValue: function (value)
20921 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
20924 if (value.length == 0) {
20925 if (this.allowBlank) {
20926 this.clearInvalid();
20930 this.markInvalid(this.errors.PwdEmpty);
20931 this.errorMsg = this.errors.PwdEmpty;
20939 if ('[\x21-\x7e]*'.match(value)) {
20940 this.markInvalid(this.errors.PwdBadChar);
20941 this.errorMsg = this.errors.PwdBadChar;
20944 if (value.length < 6) {
20945 this.markInvalid(this.errors.PwdShort);
20946 this.errorMsg = this.errors.PwdShort;
20949 if (value.length > 16) {
20950 this.markInvalid(this.errors.PwdLong);
20951 this.errorMsg = this.errors.PwdLong;
20955 if (this.ClientSideStrongPassword(value)) {
20957 } else if (this.ClientSideMediumPassword(value)) {
20959 } else if (this.ClientSideWeakPassword(value)) {
20966 if (strength < 2) {
20967 //this.markInvalid(this.errors.TooWeak);
20968 this.errorMsg = this.errors.TooWeak;
20973 console.log('strength2: ' + strength);
20975 //var pm = this.trigger.child('div/div/div').dom;
20977 var pm = this.trigger.child('div/div');
20978 pm.removeClass(this.meterClass);
20979 pm.addClass(this.meterClass[strength]);
20981 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
20983 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
20985 this.errorMsg = '';
20989 CharacterSetChecks: function (type)
20992 this.fResult = false;
20995 isctype: function (character, type)
20998 case this.kCapitalLetter:
20999 if (character >= 'A' && character <= 'Z') {
21004 case this.kSmallLetter:
21005 if (character >= 'a' && character <= 'z') {
21011 if (character >= '0' && character <= '9') {
21016 case this.kPunctuation:
21017 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21028 IsLongEnough: function (pwd, size)
21030 return !(pwd == null || isNaN(size) || pwd.length < size);
21033 SpansEnoughCharacterSets: function (word, nb)
21035 if (!this.IsLongEnough(word, nb))
21040 var characterSetChecks = new Array(
21041 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21042 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21045 for (var index = 0; index < word.length; ++index) {
21046 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21047 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21048 characterSetChecks[nCharSet].fResult = true;
21055 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21056 if (characterSetChecks[nCharSet].fResult) {
21061 if (nCharSets < nb) {
21067 ClientSideStrongPassword: function (pwd)
21069 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21072 ClientSideMediumPassword: function (pwd)
21074 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21077 ClientSideWeakPassword: function (pwd)
21079 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21082 })//<script type="text/javascript">
21085 * Based Ext JS Library 1.1.1
21086 * Copyright(c) 2006-2007, Ext JS, LLC.
21092 * @class Roo.HtmlEditorCore
21093 * @extends Roo.Component
21094 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21096 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21099 Roo.HtmlEditorCore = function(config){
21102 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21107 * @event initialize
21108 * Fires when the editor is fully initialized (including the iframe)
21109 * @param {Roo.HtmlEditorCore} this
21114 * Fires when the editor is first receives the focus. Any insertion must wait
21115 * until after this event.
21116 * @param {Roo.HtmlEditorCore} this
21120 * @event beforesync
21121 * Fires before the textarea is updated with content from the editor iframe. Return false
21122 * to cancel the sync.
21123 * @param {Roo.HtmlEditorCore} this
21124 * @param {String} html
21128 * @event beforepush
21129 * Fires before the iframe editor is updated with content from the textarea. Return false
21130 * to cancel the push.
21131 * @param {Roo.HtmlEditorCore} this
21132 * @param {String} html
21137 * Fires when the textarea is updated with content from the editor iframe.
21138 * @param {Roo.HtmlEditorCore} this
21139 * @param {String} html
21144 * Fires when the iframe editor is updated with content from the textarea.
21145 * @param {Roo.HtmlEditorCore} this
21146 * @param {String} html
21151 * @event editorevent
21152 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21153 * @param {Roo.HtmlEditorCore} this
21159 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21161 // defaults : white / black...
21162 this.applyBlacklists();
21169 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
21173 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
21179 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21184 * @cfg {Number} height (in pixels)
21188 * @cfg {Number} width (in pixels)
21193 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21196 stylesheets: false,
21201 // private properties
21202 validationEvent : false,
21204 initialized : false,
21206 sourceEditMode : false,
21207 onFocus : Roo.emptyFn,
21209 hideMode:'offsets',
21213 // blacklist + whitelisted elements..
21220 * Protected method that will not generally be called directly. It
21221 * is called when the editor initializes the iframe with HTML contents. Override this method if you
21222 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21224 getDocMarkup : function(){
21228 // inherit styels from page...??
21229 if (this.stylesheets === false) {
21231 Roo.get(document.head).select('style').each(function(node) {
21232 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21235 Roo.get(document.head).select('link').each(function(node) {
21236 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21239 } else if (!this.stylesheets.length) {
21241 st = '<style type="text/css">' +
21242 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21248 st += '<style type="text/css">' +
21249 'IMG { cursor: pointer } ' +
21253 return '<html><head>' + st +
21254 //<style type="text/css">' +
21255 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21257 ' </head><body class="roo-htmleditor-body"></body></html>';
21261 onRender : function(ct, position)
21264 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21265 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21268 this.el.dom.style.border = '0 none';
21269 this.el.dom.setAttribute('tabIndex', -1);
21270 this.el.addClass('x-hidden hide');
21274 if(Roo.isIE){ // fix IE 1px bogus margin
21275 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21279 this.frameId = Roo.id();
21283 var iframe = this.owner.wrap.createChild({
21285 cls: 'form-control', // bootstrap..
21287 name: this.frameId,
21288 frameBorder : 'no',
21289 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
21294 this.iframe = iframe.dom;
21296 this.assignDocWin();
21298 this.doc.designMode = 'on';
21301 this.doc.write(this.getDocMarkup());
21305 var task = { // must defer to wait for browser to be ready
21307 //console.log("run task?" + this.doc.readyState);
21308 this.assignDocWin();
21309 if(this.doc.body || this.doc.readyState == 'complete'){
21311 this.doc.designMode="on";
21315 Roo.TaskMgr.stop(task);
21316 this.initEditor.defer(10, this);
21323 Roo.TaskMgr.start(task);
21328 onResize : function(w, h)
21330 Roo.log('resize: ' +w + ',' + h );
21331 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21335 if(typeof w == 'number'){
21337 this.iframe.style.width = w + 'px';
21339 if(typeof h == 'number'){
21341 this.iframe.style.height = h + 'px';
21343 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21350 * Toggles the editor between standard and source edit mode.
21351 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21353 toggleSourceEdit : function(sourceEditMode){
21355 this.sourceEditMode = sourceEditMode === true;
21357 if(this.sourceEditMode){
21359 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
21362 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21363 //this.iframe.className = '';
21366 //this.setSize(this.owner.wrap.getSize());
21367 //this.fireEvent('editmodechange', this, this.sourceEditMode);
21374 * Protected method that will not generally be called directly. If you need/want
21375 * custom HTML cleanup, this is the method you should override.
21376 * @param {String} html The HTML to be cleaned
21377 * return {String} The cleaned HTML
21379 cleanHtml : function(html){
21380 html = String(html);
21381 if(html.length > 5){
21382 if(Roo.isSafari){ // strip safari nonsense
21383 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21386 if(html == ' '){
21393 * HTML Editor -> Textarea
21394 * Protected method that will not generally be called directly. Syncs the contents
21395 * of the editor iframe with the textarea.
21397 syncValue : function(){
21398 if(this.initialized){
21399 var bd = (this.doc.body || this.doc.documentElement);
21400 //this.cleanUpPaste(); -- this is done else where and causes havoc..
21401 var html = bd.innerHTML;
21403 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21404 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21406 html = '<div style="'+m[0]+'">' + html + '</div>';
21409 html = this.cleanHtml(html);
21410 // fix up the special chars.. normaly like back quotes in word...
21411 // however we do not want to do this with chinese..
21412 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21413 var cc = b.charCodeAt();
21415 (cc >= 0x4E00 && cc < 0xA000 ) ||
21416 (cc >= 0x3400 && cc < 0x4E00 ) ||
21417 (cc >= 0xf900 && cc < 0xfb00 )
21423 if(this.owner.fireEvent('beforesync', this, html) !== false){
21424 this.el.dom.value = html;
21425 this.owner.fireEvent('sync', this, html);
21431 * Protected method that will not generally be called directly. Pushes the value of the textarea
21432 * into the iframe editor.
21434 pushValue : function(){
21435 if(this.initialized){
21436 var v = this.el.dom.value.trim();
21438 // if(v.length < 1){
21442 if(this.owner.fireEvent('beforepush', this, v) !== false){
21443 var d = (this.doc.body || this.doc.documentElement);
21445 this.cleanUpPaste();
21446 this.el.dom.value = d.innerHTML;
21447 this.owner.fireEvent('push', this, v);
21453 deferFocus : function(){
21454 this.focus.defer(10, this);
21458 focus : function(){
21459 if(this.win && !this.sourceEditMode){
21466 assignDocWin: function()
21468 var iframe = this.iframe;
21471 this.doc = iframe.contentWindow.document;
21472 this.win = iframe.contentWindow;
21474 // if (!Roo.get(this.frameId)) {
21477 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21478 // this.win = Roo.get(this.frameId).dom.contentWindow;
21480 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21484 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21485 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21490 initEditor : function(){
21491 //console.log("INIT EDITOR");
21492 this.assignDocWin();
21496 this.doc.designMode="on";
21498 this.doc.write(this.getDocMarkup());
21501 var dbody = (this.doc.body || this.doc.documentElement);
21502 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21503 // this copies styles from the containing element into thsi one..
21504 // not sure why we need all of this..
21505 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21507 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21508 //ss['background-attachment'] = 'fixed'; // w3c
21509 dbody.bgProperties = 'fixed'; // ie
21510 //Roo.DomHelper.applyStyles(dbody, ss);
21511 Roo.EventManager.on(this.doc, {
21512 //'mousedown': this.onEditorEvent,
21513 'mouseup': this.onEditorEvent,
21514 'dblclick': this.onEditorEvent,
21515 'click': this.onEditorEvent,
21516 'keyup': this.onEditorEvent,
21521 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21523 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21524 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21526 this.initialized = true;
21528 this.owner.fireEvent('initialize', this);
21533 onDestroy : function(){
21539 //for (var i =0; i < this.toolbars.length;i++) {
21540 // // fixme - ask toolbars for heights?
21541 // this.toolbars[i].onDestroy();
21544 //this.wrap.dom.innerHTML = '';
21545 //this.wrap.remove();
21550 onFirstFocus : function(){
21552 this.assignDocWin();
21555 this.activated = true;
21558 if(Roo.isGecko){ // prevent silly gecko errors
21560 var s = this.win.getSelection();
21561 if(!s.focusNode || s.focusNode.nodeType != 3){
21562 var r = s.getRangeAt(0);
21563 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21568 this.execCmd('useCSS', true);
21569 this.execCmd('styleWithCSS', false);
21572 this.owner.fireEvent('activate', this);
21576 adjustFont: function(btn){
21577 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21578 //if(Roo.isSafari){ // safari
21581 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21582 if(Roo.isSafari){ // safari
21583 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21584 v = (v < 10) ? 10 : v;
21585 v = (v > 48) ? 48 : v;
21586 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21591 v = Math.max(1, v+adjust);
21593 this.execCmd('FontSize', v );
21596 onEditorEvent : function(e)
21598 this.owner.fireEvent('editorevent', this, e);
21599 // this.updateToolbar();
21600 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21603 insertTag : function(tg)
21605 // could be a bit smarter... -> wrap the current selected tRoo..
21606 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
21608 range = this.createRange(this.getSelection());
21609 var wrappingNode = this.doc.createElement(tg.toLowerCase());
21610 wrappingNode.appendChild(range.extractContents());
21611 range.insertNode(wrappingNode);
21618 this.execCmd("formatblock", tg);
21622 insertText : function(txt)
21626 var range = this.createRange();
21627 range.deleteContents();
21628 //alert(Sender.getAttribute('label'));
21630 range.insertNode(this.doc.createTextNode(txt));
21636 * Executes a Midas editor command on the editor document and performs necessary focus and
21637 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21638 * @param {String} cmd The Midas command
21639 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21641 relayCmd : function(cmd, value){
21643 this.execCmd(cmd, value);
21644 this.owner.fireEvent('editorevent', this);
21645 //this.updateToolbar();
21646 this.owner.deferFocus();
21650 * Executes a Midas editor command directly on the editor document.
21651 * For visual commands, you should use {@link #relayCmd} instead.
21652 * <b>This should only be called after the editor is initialized.</b>
21653 * @param {String} cmd The Midas command
21654 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21656 execCmd : function(cmd, value){
21657 this.doc.execCommand(cmd, false, value === undefined ? null : value);
21664 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21666 * @param {String} text | dom node..
21668 insertAtCursor : function(text)
21671 if(!this.activated){
21677 var r = this.doc.selection.createRange();
21688 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21692 // from jquery ui (MIT licenced)
21694 var win = this.win;
21696 if (win.getSelection && win.getSelection().getRangeAt) {
21697 range = win.getSelection().getRangeAt(0);
21698 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21699 range.insertNode(node);
21700 } else if (win.document.selection && win.document.selection.createRange) {
21701 // no firefox support
21702 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21703 win.document.selection.createRange().pasteHTML(txt);
21705 // no firefox support
21706 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21707 this.execCmd('InsertHTML', txt);
21716 mozKeyPress : function(e){
21718 var c = e.getCharCode(), cmd;
21721 c = String.fromCharCode(c).toLowerCase();
21735 this.cleanUpPaste.defer(100, this);
21743 e.preventDefault();
21751 fixKeys : function(){ // load time branching for fastest keydown performance
21753 return function(e){
21754 var k = e.getKey(), r;
21757 r = this.doc.selection.createRange();
21760 r.pasteHTML('    ');
21767 r = this.doc.selection.createRange();
21769 var target = r.parentElement();
21770 if(!target || target.tagName.toLowerCase() != 'li'){
21772 r.pasteHTML('<br />');
21778 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21779 this.cleanUpPaste.defer(100, this);
21785 }else if(Roo.isOpera){
21786 return function(e){
21787 var k = e.getKey();
21791 this.execCmd('InsertHTML','    ');
21794 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21795 this.cleanUpPaste.defer(100, this);
21800 }else if(Roo.isSafari){
21801 return function(e){
21802 var k = e.getKey();
21806 this.execCmd('InsertText','\t');
21810 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21811 this.cleanUpPaste.defer(100, this);
21819 getAllAncestors: function()
21821 var p = this.getSelectedNode();
21824 a.push(p); // push blank onto stack..
21825 p = this.getParentElement();
21829 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21833 a.push(this.doc.body);
21837 lastSelNode : false,
21840 getSelection : function()
21842 this.assignDocWin();
21843 return Roo.isIE ? this.doc.selection : this.win.getSelection();
21846 getSelectedNode: function()
21848 // this may only work on Gecko!!!
21850 // should we cache this!!!!
21855 var range = this.createRange(this.getSelection()).cloneRange();
21858 var parent = range.parentElement();
21860 var testRange = range.duplicate();
21861 testRange.moveToElementText(parent);
21862 if (testRange.inRange(range)) {
21865 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21868 parent = parent.parentElement;
21873 // is ancestor a text element.
21874 var ac = range.commonAncestorContainer;
21875 if (ac.nodeType == 3) {
21876 ac = ac.parentNode;
21879 var ar = ac.childNodes;
21882 var other_nodes = [];
21883 var has_other_nodes = false;
21884 for (var i=0;i<ar.length;i++) {
21885 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
21888 // fullly contained node.
21890 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21895 // probably selected..
21896 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21897 other_nodes.push(ar[i]);
21901 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
21906 has_other_nodes = true;
21908 if (!nodes.length && other_nodes.length) {
21909 nodes= other_nodes;
21911 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21917 createRange: function(sel)
21919 // this has strange effects when using with
21920 // top toolbar - not sure if it's a great idea.
21921 //this.editor.contentWindow.focus();
21922 if (typeof sel != "undefined") {
21924 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21926 return this.doc.createRange();
21929 return this.doc.createRange();
21932 getParentElement: function()
21935 this.assignDocWin();
21936 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21938 var range = this.createRange(sel);
21941 var p = range.commonAncestorContainer;
21942 while (p.nodeType == 3) { // text node
21953 * Range intersection.. the hard stuff...
21957 * [ -- selected range --- ]
21961 * if end is before start or hits it. fail.
21962 * if start is after end or hits it fail.
21964 * if either hits (but other is outside. - then it's not
21970 // @see http://www.thismuchiknow.co.uk/?p=64.
21971 rangeIntersectsNode : function(range, node)
21973 var nodeRange = node.ownerDocument.createRange();
21975 nodeRange.selectNode(node);
21977 nodeRange.selectNodeContents(node);
21980 var rangeStartRange = range.cloneRange();
21981 rangeStartRange.collapse(true);
21983 var rangeEndRange = range.cloneRange();
21984 rangeEndRange.collapse(false);
21986 var nodeStartRange = nodeRange.cloneRange();
21987 nodeStartRange.collapse(true);
21989 var nodeEndRange = nodeRange.cloneRange();
21990 nodeEndRange.collapse(false);
21992 return rangeStartRange.compareBoundaryPoints(
21993 Range.START_TO_START, nodeEndRange) == -1 &&
21994 rangeEndRange.compareBoundaryPoints(
21995 Range.START_TO_START, nodeStartRange) == 1;
21999 rangeCompareNode : function(range, node)
22001 var nodeRange = node.ownerDocument.createRange();
22003 nodeRange.selectNode(node);
22005 nodeRange.selectNodeContents(node);
22009 range.collapse(true);
22011 nodeRange.collapse(true);
22013 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22014 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
22016 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22018 var nodeIsBefore = ss == 1;
22019 var nodeIsAfter = ee == -1;
22021 if (nodeIsBefore && nodeIsAfter) {
22024 if (!nodeIsBefore && nodeIsAfter) {
22025 return 1; //right trailed.
22028 if (nodeIsBefore && !nodeIsAfter) {
22029 return 2; // left trailed.
22035 // private? - in a new class?
22036 cleanUpPaste : function()
22038 // cleans up the whole document..
22039 Roo.log('cleanuppaste');
22041 this.cleanUpChildren(this.doc.body);
22042 var clean = this.cleanWordChars(this.doc.body.innerHTML);
22043 if (clean != this.doc.body.innerHTML) {
22044 this.doc.body.innerHTML = clean;
22049 cleanWordChars : function(input) {// change the chars to hex code
22050 var he = Roo.HtmlEditorCore;
22052 var output = input;
22053 Roo.each(he.swapCodes, function(sw) {
22054 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22056 output = output.replace(swapper, sw[1]);
22063 cleanUpChildren : function (n)
22065 if (!n.childNodes.length) {
22068 for (var i = n.childNodes.length-1; i > -1 ; i--) {
22069 this.cleanUpChild(n.childNodes[i]);
22076 cleanUpChild : function (node)
22079 //console.log(node);
22080 if (node.nodeName == "#text") {
22081 // clean up silly Windows -- stuff?
22084 if (node.nodeName == "#comment") {
22085 node.parentNode.removeChild(node);
22086 // clean up silly Windows -- stuff?
22089 var lcname = node.tagName.toLowerCase();
22090 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22091 // whitelist of tags..
22093 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22095 node.parentNode.removeChild(node);
22100 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22102 // remove <a name=....> as rendering on yahoo mailer is borked with this.
22103 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22105 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22106 // remove_keep_children = true;
22109 if (remove_keep_children) {
22110 this.cleanUpChildren(node);
22111 // inserts everything just before this node...
22112 while (node.childNodes.length) {
22113 var cn = node.childNodes[0];
22114 node.removeChild(cn);
22115 node.parentNode.insertBefore(cn, node);
22117 node.parentNode.removeChild(node);
22121 if (!node.attributes || !node.attributes.length) {
22122 this.cleanUpChildren(node);
22126 function cleanAttr(n,v)
22129 if (v.match(/^\./) || v.match(/^\//)) {
22132 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
22135 if (v.match(/^#/)) {
22138 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22139 node.removeAttribute(n);
22143 var cwhite = this.cwhite;
22144 var cblack = this.cblack;
22146 function cleanStyle(n,v)
22148 if (v.match(/expression/)) { //XSS?? should we even bother..
22149 node.removeAttribute(n);
22153 var parts = v.split(/;/);
22156 Roo.each(parts, function(p) {
22157 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22161 var l = p.split(':').shift().replace(/\s+/g,'');
22162 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22164 if ( cwhite.length && cblack.indexOf(l) > -1) {
22165 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22166 //node.removeAttribute(n);
22170 // only allow 'c whitelisted system attributes'
22171 if ( cwhite.length && cwhite.indexOf(l) < 0) {
22172 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22173 //node.removeAttribute(n);
22183 if (clean.length) {
22184 node.setAttribute(n, clean.join(';'));
22186 node.removeAttribute(n);
22192 for (var i = node.attributes.length-1; i > -1 ; i--) {
22193 var a = node.attributes[i];
22196 if (a.name.toLowerCase().substr(0,2)=='on') {
22197 node.removeAttribute(a.name);
22200 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22201 node.removeAttribute(a.name);
22204 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22205 cleanAttr(a.name,a.value); // fixme..
22208 if (a.name == 'style') {
22209 cleanStyle(a.name,a.value);
22212 /// clean up MS crap..
22213 // tecnically this should be a list of valid class'es..
22216 if (a.name == 'class') {
22217 if (a.value.match(/^Mso/)) {
22218 node.className = '';
22221 if (a.value.match(/^body$/)) {
22222 node.className = '';
22233 this.cleanUpChildren(node);
22239 * Clean up MS wordisms...
22241 cleanWord : function(node)
22246 this.cleanWord(this.doc.body);
22249 if (node.nodeName == "#text") {
22250 // clean up silly Windows -- stuff?
22253 if (node.nodeName == "#comment") {
22254 node.parentNode.removeChild(node);
22255 // clean up silly Windows -- stuff?
22259 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22260 node.parentNode.removeChild(node);
22264 // remove - but keep children..
22265 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22266 while (node.childNodes.length) {
22267 var cn = node.childNodes[0];
22268 node.removeChild(cn);
22269 node.parentNode.insertBefore(cn, node);
22271 node.parentNode.removeChild(node);
22272 this.iterateChildren(node, this.cleanWord);
22276 if (node.className.length) {
22278 var cn = node.className.split(/\W+/);
22280 Roo.each(cn, function(cls) {
22281 if (cls.match(/Mso[a-zA-Z]+/)) {
22286 node.className = cna.length ? cna.join(' ') : '';
22288 node.removeAttribute("class");
22292 if (node.hasAttribute("lang")) {
22293 node.removeAttribute("lang");
22296 if (node.hasAttribute("style")) {
22298 var styles = node.getAttribute("style").split(";");
22300 Roo.each(styles, function(s) {
22301 if (!s.match(/:/)) {
22304 var kv = s.split(":");
22305 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22308 // what ever is left... we allow.
22311 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22312 if (!nstyle.length) {
22313 node.removeAttribute('style');
22316 this.iterateChildren(node, this.cleanWord);
22322 * iterateChildren of a Node, calling fn each time, using this as the scole..
22323 * @param {DomNode} node node to iterate children of.
22324 * @param {Function} fn method of this class to call on each item.
22326 iterateChildren : function(node, fn)
22328 if (!node.childNodes.length) {
22331 for (var i = node.childNodes.length-1; i > -1 ; i--) {
22332 fn.call(this, node.childNodes[i])
22338 * cleanTableWidths.
22340 * Quite often pasting from word etc.. results in tables with column and widths.
22341 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22344 cleanTableWidths : function(node)
22349 this.cleanTableWidths(this.doc.body);
22354 if (node.nodeName == "#text" || node.nodeName == "#comment") {
22357 Roo.log(node.tagName);
22358 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22359 this.iterateChildren(node, this.cleanTableWidths);
22362 if (node.hasAttribute('width')) {
22363 node.removeAttribute('width');
22367 if (node.hasAttribute("style")) {
22370 var styles = node.getAttribute("style").split(";");
22372 Roo.each(styles, function(s) {
22373 if (!s.match(/:/)) {
22376 var kv = s.split(":");
22377 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22380 // what ever is left... we allow.
22383 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22384 if (!nstyle.length) {
22385 node.removeAttribute('style');
22389 this.iterateChildren(node, this.cleanTableWidths);
22397 domToHTML : function(currentElement, depth, nopadtext) {
22399 depth = depth || 0;
22400 nopadtext = nopadtext || false;
22402 if (!currentElement) {
22403 return this.domToHTML(this.doc.body);
22406 //Roo.log(currentElement);
22408 var allText = false;
22409 var nodeName = currentElement.nodeName;
22410 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22412 if (nodeName == '#text') {
22414 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22419 if (nodeName != 'BODY') {
22422 // Prints the node tagName, such as <A>, <IMG>, etc
22425 for(i = 0; i < currentElement.attributes.length;i++) {
22427 var aname = currentElement.attributes.item(i).name;
22428 if (!currentElement.attributes.item(i).value.length) {
22431 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22434 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22443 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22446 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22451 // Traverse the tree
22453 var currentElementChild = currentElement.childNodes.item(i);
22454 var allText = true;
22455 var innerHTML = '';
22457 while (currentElementChild) {
22458 // Formatting code (indent the tree so it looks nice on the screen)
22459 var nopad = nopadtext;
22460 if (lastnode == 'SPAN') {
22464 if (currentElementChild.nodeName == '#text') {
22465 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22466 toadd = nopadtext ? toadd : toadd.trim();
22467 if (!nopad && toadd.length > 80) {
22468 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
22470 innerHTML += toadd;
22473 currentElementChild = currentElement.childNodes.item(i);
22479 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
22481 // Recursively traverse the tree structure of the child node
22482 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
22483 lastnode = currentElementChild.nodeName;
22485 currentElementChild=currentElement.childNodes.item(i);
22491 // The remaining code is mostly for formatting the tree
22492 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
22497 ret+= "</"+tagName+">";
22503 applyBlacklists : function()
22505 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
22506 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
22510 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22511 if (b.indexOf(tag) > -1) {
22514 this.white.push(tag);
22518 Roo.each(w, function(tag) {
22519 if (b.indexOf(tag) > -1) {
22522 if (this.white.indexOf(tag) > -1) {
22525 this.white.push(tag);
22530 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22531 if (w.indexOf(tag) > -1) {
22534 this.black.push(tag);
22538 Roo.each(b, function(tag) {
22539 if (w.indexOf(tag) > -1) {
22542 if (this.black.indexOf(tag) > -1) {
22545 this.black.push(tag);
22550 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
22551 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
22555 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22556 if (b.indexOf(tag) > -1) {
22559 this.cwhite.push(tag);
22563 Roo.each(w, function(tag) {
22564 if (b.indexOf(tag) > -1) {
22567 if (this.cwhite.indexOf(tag) > -1) {
22570 this.cwhite.push(tag);
22575 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22576 if (w.indexOf(tag) > -1) {
22579 this.cblack.push(tag);
22583 Roo.each(b, function(tag) {
22584 if (w.indexOf(tag) > -1) {
22587 if (this.cblack.indexOf(tag) > -1) {
22590 this.cblack.push(tag);
22595 setStylesheets : function(stylesheets)
22597 if(typeof(stylesheets) == 'string'){
22598 Roo.get(this.iframe.contentDocument.head).createChild({
22600 rel : 'stylesheet',
22609 Roo.each(stylesheets, function(s) {
22614 Roo.get(_this.iframe.contentDocument.head).createChild({
22616 rel : 'stylesheet',
22625 removeStylesheets : function()
22629 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22634 // hide stuff that is not compatible
22648 * @event specialkey
22652 * @cfg {String} fieldClass @hide
22655 * @cfg {String} focusClass @hide
22658 * @cfg {String} autoCreate @hide
22661 * @cfg {String} inputType @hide
22664 * @cfg {String} invalidClass @hide
22667 * @cfg {String} invalidText @hide
22670 * @cfg {String} msgFx @hide
22673 * @cfg {String} validateOnBlur @hide
22677 Roo.HtmlEditorCore.white = [
22678 'area', 'br', 'img', 'input', 'hr', 'wbr',
22680 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
22681 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
22682 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
22683 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
22684 'table', 'ul', 'xmp',
22686 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
22689 'dir', 'menu', 'ol', 'ul', 'dl',
22695 Roo.HtmlEditorCore.black = [
22696 // 'embed', 'object', // enable - backend responsiblity to clean thiese
22698 'base', 'basefont', 'bgsound', 'blink', 'body',
22699 'frame', 'frameset', 'head', 'html', 'ilayer',
22700 'iframe', 'layer', 'link', 'meta', 'object',
22701 'script', 'style' ,'title', 'xml' // clean later..
22703 Roo.HtmlEditorCore.clean = [
22704 'script', 'style', 'title', 'xml'
22706 Roo.HtmlEditorCore.remove = [
22711 Roo.HtmlEditorCore.ablack = [
22715 Roo.HtmlEditorCore.aclean = [
22716 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
22720 Roo.HtmlEditorCore.pwhite= [
22721 'http', 'https', 'mailto'
22724 // white listed style attributes.
22725 Roo.HtmlEditorCore.cwhite= [
22726 // 'text-align', /// default is to allow most things..
22732 // black listed style attributes.
22733 Roo.HtmlEditorCore.cblack= [
22734 // 'font-size' -- this can be set by the project
22738 Roo.HtmlEditorCore.swapCodes =[
22757 * @class Roo.bootstrap.HtmlEditor
22758 * @extends Roo.bootstrap.TextArea
22759 * Bootstrap HtmlEditor class
22762 * Create a new HtmlEditor
22763 * @param {Object} config The config object
22766 Roo.bootstrap.HtmlEditor = function(config){
22767 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
22768 if (!this.toolbars) {
22769 this.toolbars = [];
22772 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22775 * @event initialize
22776 * Fires when the editor is fully initialized (including the iframe)
22777 * @param {HtmlEditor} this
22782 * Fires when the editor is first receives the focus. Any insertion must wait
22783 * until after this event.
22784 * @param {HtmlEditor} this
22788 * @event beforesync
22789 * Fires before the textarea is updated with content from the editor iframe. Return false
22790 * to cancel the sync.
22791 * @param {HtmlEditor} this
22792 * @param {String} html
22796 * @event beforepush
22797 * Fires before the iframe editor is updated with content from the textarea. Return false
22798 * to cancel the push.
22799 * @param {HtmlEditor} this
22800 * @param {String} html
22805 * Fires when the textarea is updated with content from the editor iframe.
22806 * @param {HtmlEditor} this
22807 * @param {String} html
22812 * Fires when the iframe editor is updated with content from the textarea.
22813 * @param {HtmlEditor} this
22814 * @param {String} html
22818 * @event editmodechange
22819 * Fires when the editor switches edit modes
22820 * @param {HtmlEditor} this
22821 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22823 editmodechange: true,
22825 * @event editorevent
22826 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22827 * @param {HtmlEditor} this
22831 * @event firstfocus
22832 * Fires when on first focus - needed by toolbars..
22833 * @param {HtmlEditor} this
22838 * Auto save the htmlEditor value as a file into Events
22839 * @param {HtmlEditor} this
22843 * @event savedpreview
22844 * preview the saved version of htmlEditor
22845 * @param {HtmlEditor} this
22852 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
22856 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22861 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
22866 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
22871 * @cfg {Number} height (in pixels)
22875 * @cfg {Number} width (in pixels)
22880 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22883 stylesheets: false,
22888 // private properties
22889 validationEvent : false,
22891 initialized : false,
22894 onFocus : Roo.emptyFn,
22896 hideMode:'offsets',
22898 tbContainer : false,
22900 toolbarContainer :function() {
22901 return this.wrap.select('.x-html-editor-tb',true).first();
22905 * Protected method that will not generally be called directly. It
22906 * is called when the editor creates its toolbar. Override this method if you need to
22907 * add custom toolbar buttons.
22908 * @param {HtmlEditor} editor
22910 createToolbar : function(){
22911 Roo.log('renewing');
22912 Roo.log("create toolbars");
22914 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
22915 this.toolbars[0].render(this.toolbarContainer());
22919 // if (!editor.toolbars || !editor.toolbars.length) {
22920 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
22923 // for (var i =0 ; i < editor.toolbars.length;i++) {
22924 // editor.toolbars[i] = Roo.factory(
22925 // typeof(editor.toolbars[i]) == 'string' ?
22926 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
22927 // Roo.bootstrap.HtmlEditor);
22928 // editor.toolbars[i].init(editor);
22934 onRender : function(ct, position)
22936 // Roo.log("Call onRender: " + this.xtype);
22938 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
22940 this.wrap = this.inputEl().wrap({
22941 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22944 this.editorcore.onRender(ct, position);
22946 if (this.resizable) {
22947 this.resizeEl = new Roo.Resizable(this.wrap, {
22951 minHeight : this.height,
22952 height: this.height,
22953 handles : this.resizable,
22956 resize : function(r, w, h) {
22957 _t.onResize(w,h); // -something
22963 this.createToolbar(this);
22966 if(!this.width && this.resizable){
22967 this.setSize(this.wrap.getSize());
22969 if (this.resizeEl) {
22970 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22971 // should trigger onReize..
22977 onResize : function(w, h)
22979 Roo.log('resize: ' +w + ',' + h );
22980 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
22984 if(this.inputEl() ){
22985 if(typeof w == 'number'){
22986 var aw = w - this.wrap.getFrameWidth('lr');
22987 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
22990 if(typeof h == 'number'){
22991 var tbh = -11; // fixme it needs to tool bar size!
22992 for (var i =0; i < this.toolbars.length;i++) {
22993 // fixme - ask toolbars for heights?
22994 tbh += this.toolbars[i].el.getHeight();
22995 //if (this.toolbars[i].footer) {
22996 // tbh += this.toolbars[i].footer.el.getHeight();
23004 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23005 ah -= 5; // knock a few pixes off for look..
23006 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23010 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23011 this.editorcore.onResize(ew,eh);
23016 * Toggles the editor between standard and source edit mode.
23017 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23019 toggleSourceEdit : function(sourceEditMode)
23021 this.editorcore.toggleSourceEdit(sourceEditMode);
23023 if(this.editorcore.sourceEditMode){
23024 Roo.log('editor - showing textarea');
23027 // Roo.log(this.syncValue());
23029 this.inputEl().removeClass(['hide', 'x-hidden']);
23030 this.inputEl().dom.removeAttribute('tabIndex');
23031 this.inputEl().focus();
23033 Roo.log('editor - hiding textarea');
23035 // Roo.log(this.pushValue());
23038 this.inputEl().addClass(['hide', 'x-hidden']);
23039 this.inputEl().dom.setAttribute('tabIndex', -1);
23040 //this.deferFocus();
23043 if(this.resizable){
23044 this.setSize(this.wrap.getSize());
23047 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23050 // private (for BoxComponent)
23051 adjustSize : Roo.BoxComponent.prototype.adjustSize,
23053 // private (for BoxComponent)
23054 getResizeEl : function(){
23058 // private (for BoxComponent)
23059 getPositionEl : function(){
23064 initEvents : function(){
23065 this.originalValue = this.getValue();
23069 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23072 // markInvalid : Roo.emptyFn,
23074 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23077 // clearInvalid : Roo.emptyFn,
23079 setValue : function(v){
23080 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23081 this.editorcore.pushValue();
23086 deferFocus : function(){
23087 this.focus.defer(10, this);
23091 focus : function(){
23092 this.editorcore.focus();
23098 onDestroy : function(){
23104 for (var i =0; i < this.toolbars.length;i++) {
23105 // fixme - ask toolbars for heights?
23106 this.toolbars[i].onDestroy();
23109 this.wrap.dom.innerHTML = '';
23110 this.wrap.remove();
23115 onFirstFocus : function(){
23116 //Roo.log("onFirstFocus");
23117 this.editorcore.onFirstFocus();
23118 for (var i =0; i < this.toolbars.length;i++) {
23119 this.toolbars[i].onFirstFocus();
23125 syncValue : function()
23127 this.editorcore.syncValue();
23130 pushValue : function()
23132 this.editorcore.pushValue();
23136 // hide stuff that is not compatible
23150 * @event specialkey
23154 * @cfg {String} fieldClass @hide
23157 * @cfg {String} focusClass @hide
23160 * @cfg {String} autoCreate @hide
23163 * @cfg {String} inputType @hide
23166 * @cfg {String} invalidClass @hide
23169 * @cfg {String} invalidText @hide
23172 * @cfg {String} msgFx @hide
23175 * @cfg {String} validateOnBlur @hide
23184 Roo.namespace('Roo.bootstrap.htmleditor');
23186 * @class Roo.bootstrap.HtmlEditorToolbar1
23191 new Roo.bootstrap.HtmlEditor({
23194 new Roo.bootstrap.HtmlEditorToolbar1({
23195 disable : { fonts: 1 , format: 1, ..., ... , ...],
23201 * @cfg {Object} disable List of elements to disable..
23202 * @cfg {Array} btns List of additional buttons.
23206 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23209 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23212 Roo.apply(this, config);
23214 // default disabled, based on 'good practice'..
23215 this.disable = this.disable || {};
23216 Roo.applyIf(this.disable, {
23219 specialElements : true
23221 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23223 this.editor = config.editor;
23224 this.editorcore = config.editor.editorcore;
23226 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23228 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23229 // dont call parent... till later.
23231 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
23236 editorcore : false,
23241 "h1","h2","h3","h4","h5","h6",
23243 "abbr", "acronym", "address", "cite", "samp", "var",
23247 onRender : function(ct, position)
23249 // Roo.log("Call onRender: " + this.xtype);
23251 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23253 this.el.dom.style.marginBottom = '0';
23255 var editorcore = this.editorcore;
23256 var editor= this.editor;
23259 var btn = function(id,cmd , toggle, handler, html){
23261 var event = toggle ? 'toggle' : 'click';
23266 xns: Roo.bootstrap,
23269 enableToggle:toggle !== false,
23271 pressed : toggle ? false : null,
23274 a.listeners[toggle ? 'toggle' : 'click'] = function() {
23275 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
23281 // var cb_box = function...
23286 xns: Roo.bootstrap,
23287 glyphicon : 'font',
23291 xns: Roo.bootstrap,
23295 Roo.each(this.formats, function(f) {
23296 style.menu.items.push({
23298 xns: Roo.bootstrap,
23299 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23304 editorcore.insertTag(this.tagname);
23311 children.push(style);
23313 btn('bold',false,true);
23314 btn('italic',false,true);
23315 btn('align-left', 'justifyleft',true);
23316 btn('align-center', 'justifycenter',true);
23317 btn('align-right' , 'justifyright',true);
23318 btn('link', false, false, function(btn) {
23319 //Roo.log("create link?");
23320 var url = prompt(this.createLinkText, this.defaultLinkValue);
23321 if(url && url != 'http:/'+'/'){
23322 this.editorcore.relayCmd('createlink', url);
23325 btn('list','insertunorderedlist',true);
23326 btn('pencil', false,true, function(btn){
23328 this.toggleSourceEdit(btn.pressed);
23331 if (this.editor.btns.length > 0) {
23332 for (var i = 0; i<this.editor.btns.length; i++) {
23333 children.push(this.editor.btns[i]);
23341 xns: Roo.bootstrap,
23346 xns: Roo.bootstrap,
23351 cog.menu.items.push({
23353 xns: Roo.bootstrap,
23354 html : Clean styles,
23359 editorcore.insertTag(this.tagname);
23368 this.xtype = 'NavSimplebar';
23370 for(var i=0;i< children.length;i++) {
23372 this.buttons.add(this.addxtypeChild(children[i]));
23376 editor.on('editorevent', this.updateToolbar, this);
23378 onBtnClick : function(id)
23380 this.editorcore.relayCmd(id);
23381 this.editorcore.focus();
23385 * Protected method that will not generally be called directly. It triggers
23386 * a toolbar update by reading the markup state of the current selection in the editor.
23388 updateToolbar: function(){
23390 if(!this.editorcore.activated){
23391 this.editor.onFirstFocus(); // is this neeed?
23395 var btns = this.buttons;
23396 var doc = this.editorcore.doc;
23397 btns.get('bold').setActive(doc.queryCommandState('bold'));
23398 btns.get('italic').setActive(doc.queryCommandState('italic'));
23399 //btns.get('underline').setActive(doc.queryCommandState('underline'));
23401 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23402 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23403 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23405 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23406 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23409 var ans = this.editorcore.getAllAncestors();
23410 if (this.formatCombo) {
23413 var store = this.formatCombo.store;
23414 this.formatCombo.setValue("");
23415 for (var i =0; i < ans.length;i++) {
23416 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23418 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23426 // hides menus... - so this cant be on a menu...
23427 Roo.bootstrap.MenuMgr.hideAll();
23429 Roo.bootstrap.MenuMgr.hideAll();
23430 //this.editorsyncValue();
23432 onFirstFocus: function() {
23433 this.buttons.each(function(item){
23437 toggleSourceEdit : function(sourceEditMode){
23440 if(sourceEditMode){
23441 Roo.log("disabling buttons");
23442 this.buttons.each( function(item){
23443 if(item.cmd != 'pencil'){
23449 Roo.log("enabling buttons");
23450 if(this.editorcore.initialized){
23451 this.buttons.each( function(item){
23457 Roo.log("calling toggole on editor");
23458 // tell the editor that it's been pressed..
23459 this.editor.toggleSourceEdit(sourceEditMode);
23469 * @class Roo.bootstrap.Table.AbstractSelectionModel
23470 * @extends Roo.util.Observable
23471 * Abstract base class for grid SelectionModels. It provides the interface that should be
23472 * implemented by descendant classes. This class should not be directly instantiated.
23475 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23476 this.locked = false;
23477 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23481 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
23482 /** @ignore Called by the grid automatically. Do not call directly. */
23483 init : function(grid){
23489 * Locks the selections.
23492 this.locked = true;
23496 * Unlocks the selections.
23498 unlock : function(){
23499 this.locked = false;
23503 * Returns true if the selections are locked.
23504 * @return {Boolean}
23506 isLocked : function(){
23507 return this.locked;
23511 * @extends Roo.bootstrap.Table.AbstractSelectionModel
23512 * @class Roo.bootstrap.Table.RowSelectionModel
23513 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23514 * It supports multiple selections and keyboard selection/navigation.
23516 * @param {Object} config
23519 Roo.bootstrap.Table.RowSelectionModel = function(config){
23520 Roo.apply(this, config);
23521 this.selections = new Roo.util.MixedCollection(false, function(o){
23526 this.lastActive = false;
23530 * @event selectionchange
23531 * Fires when the selection changes
23532 * @param {SelectionModel} this
23534 "selectionchange" : true,
23536 * @event afterselectionchange
23537 * Fires after the selection changes (eg. by key press or clicking)
23538 * @param {SelectionModel} this
23540 "afterselectionchange" : true,
23542 * @event beforerowselect
23543 * Fires when a row is selected being selected, return false to cancel.
23544 * @param {SelectionModel} this
23545 * @param {Number} rowIndex The selected index
23546 * @param {Boolean} keepExisting False if other selections will be cleared
23548 "beforerowselect" : true,
23551 * Fires when a row is selected.
23552 * @param {SelectionModel} this
23553 * @param {Number} rowIndex The selected index
23554 * @param {Roo.data.Record} r The record
23556 "rowselect" : true,
23558 * @event rowdeselect
23559 * Fires when a row is deselected.
23560 * @param {SelectionModel} this
23561 * @param {Number} rowIndex The selected index
23563 "rowdeselect" : true
23565 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23566 this.locked = false;
23569 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
23571 * @cfg {Boolean} singleSelect
23572 * True to allow selection of only one row at a time (defaults to false)
23574 singleSelect : false,
23577 initEvents : function()
23580 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
23581 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
23582 //}else{ // allow click to work like normal
23583 // this.grid.on("rowclick", this.handleDragableRowClick, this);
23585 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
23586 this.grid.on("rowclick", this.handleMouseDown, this);
23588 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
23589 "up" : function(e){
23591 this.selectPrevious(e.shiftKey);
23592 }else if(this.last !== false && this.lastActive !== false){
23593 var last = this.last;
23594 this.selectRange(this.last, this.lastActive-1);
23595 this.grid.getView().focusRow(this.lastActive);
23596 if(last !== false){
23600 this.selectFirstRow();
23602 this.fireEvent("afterselectionchange", this);
23604 "down" : function(e){
23606 this.selectNext(e.shiftKey);
23607 }else if(this.last !== false && this.lastActive !== false){
23608 var last = this.last;
23609 this.selectRange(this.last, this.lastActive+1);
23610 this.grid.getView().focusRow(this.lastActive);
23611 if(last !== false){
23615 this.selectFirstRow();
23617 this.fireEvent("afterselectionchange", this);
23621 this.grid.store.on('load', function(){
23622 this.selections.clear();
23625 var view = this.grid.view;
23626 view.on("refresh", this.onRefresh, this);
23627 view.on("rowupdated", this.onRowUpdated, this);
23628 view.on("rowremoved", this.onRemove, this);
23633 onRefresh : function()
23635 var ds = this.grid.store, i, v = this.grid.view;
23636 var s = this.selections;
23637 s.each(function(r){
23638 if((i = ds.indexOfId(r.id)) != -1){
23647 onRemove : function(v, index, r){
23648 this.selections.remove(r);
23652 onRowUpdated : function(v, index, r){
23653 if(this.isSelected(r)){
23654 v.onRowSelect(index);
23660 * @param {Array} records The records to select
23661 * @param {Boolean} keepExisting (optional) True to keep existing selections
23663 selectRecords : function(records, keepExisting)
23666 this.clearSelections();
23668 var ds = this.grid.store;
23669 for(var i = 0, len = records.length; i < len; i++){
23670 this.selectRow(ds.indexOf(records[i]), true);
23675 * Gets the number of selected rows.
23678 getCount : function(){
23679 return this.selections.length;
23683 * Selects the first row in the grid.
23685 selectFirstRow : function(){
23690 * Select the last row.
23691 * @param {Boolean} keepExisting (optional) True to keep existing selections
23693 selectLastRow : function(keepExisting){
23694 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
23695 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
23699 * Selects the row immediately following the last selected row.
23700 * @param {Boolean} keepExisting (optional) True to keep existing selections
23702 selectNext : function(keepExisting)
23704 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
23705 this.selectRow(this.last+1, keepExisting);
23706 this.grid.getView().focusRow(this.last);
23711 * Selects the row that precedes the last selected row.
23712 * @param {Boolean} keepExisting (optional) True to keep existing selections
23714 selectPrevious : function(keepExisting){
23716 this.selectRow(this.last-1, keepExisting);
23717 this.grid.getView().focusRow(this.last);
23722 * Returns the selected records
23723 * @return {Array} Array of selected records
23725 getSelections : function(){
23726 return [].concat(this.selections.items);
23730 * Returns the first selected record.
23733 getSelected : function(){
23734 return this.selections.itemAt(0);
23739 * Clears all selections.
23741 clearSelections : function(fast)
23747 var ds = this.grid.store;
23748 var s = this.selections;
23749 s.each(function(r){
23750 this.deselectRow(ds.indexOfId(r.id));
23754 this.selections.clear();
23761 * Selects all rows.
23763 selectAll : function(){
23767 this.selections.clear();
23768 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
23769 this.selectRow(i, true);
23774 * Returns True if there is a selection.
23775 * @return {Boolean}
23777 hasSelection : function(){
23778 return this.selections.length > 0;
23782 * Returns True if the specified row is selected.
23783 * @param {Number/Record} record The record or index of the record to check
23784 * @return {Boolean}
23786 isSelected : function(index){
23787 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
23788 return (r && this.selections.key(r.id) ? true : false);
23792 * Returns True if the specified record id is selected.
23793 * @param {String} id The id of record to check
23794 * @return {Boolean}
23796 isIdSelected : function(id){
23797 return (this.selections.key(id) ? true : false);
23802 handleMouseDBClick : function(e, t){
23806 handleMouseDown : function(e, t)
23808 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
23809 if(this.isLocked() || rowIndex < 0 ){
23812 if(e.shiftKey && this.last !== false){
23813 var last = this.last;
23814 this.selectRange(last, rowIndex, e.ctrlKey);
23815 this.last = last; // reset the last
23819 var isSelected = this.isSelected(rowIndex);
23820 //Roo.log("select row:" + rowIndex);
23822 this.deselectRow(rowIndex);
23824 this.selectRow(rowIndex, true);
23828 if(e.button !== 0 && isSelected){
23829 alert('rowIndex 2: ' + rowIndex);
23830 view.focusRow(rowIndex);
23831 }else if(e.ctrlKey && isSelected){
23832 this.deselectRow(rowIndex);
23833 }else if(!isSelected){
23834 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
23835 view.focusRow(rowIndex);
23839 this.fireEvent("afterselectionchange", this);
23842 handleDragableRowClick : function(grid, rowIndex, e)
23844 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
23845 this.selectRow(rowIndex, false);
23846 grid.view.focusRow(rowIndex);
23847 this.fireEvent("afterselectionchange", this);
23852 * Selects multiple rows.
23853 * @param {Array} rows Array of the indexes of the row to select
23854 * @param {Boolean} keepExisting (optional) True to keep existing selections
23856 selectRows : function(rows, keepExisting){
23858 this.clearSelections();
23860 for(var i = 0, len = rows.length; i < len; i++){
23861 this.selectRow(rows[i], true);
23866 * Selects a range of rows. All rows in between startRow and endRow are also selected.
23867 * @param {Number} startRow The index of the first row in the range
23868 * @param {Number} endRow The index of the last row in the range
23869 * @param {Boolean} keepExisting (optional) True to retain existing selections
23871 selectRange : function(startRow, endRow, keepExisting){
23876 this.clearSelections();
23878 if(startRow <= endRow){
23879 for(var i = startRow; i <= endRow; i++){
23880 this.selectRow(i, true);
23883 for(var i = startRow; i >= endRow; i--){
23884 this.selectRow(i, true);
23890 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
23891 * @param {Number} startRow The index of the first row in the range
23892 * @param {Number} endRow The index of the last row in the range
23894 deselectRange : function(startRow, endRow, preventViewNotify){
23898 for(var i = startRow; i <= endRow; i++){
23899 this.deselectRow(i, preventViewNotify);
23905 * @param {Number} row The index of the row to select
23906 * @param {Boolean} keepExisting (optional) True to keep existing selections
23908 selectRow : function(index, keepExisting, preventViewNotify)
23910 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
23913 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
23914 if(!keepExisting || this.singleSelect){
23915 this.clearSelections();
23918 var r = this.grid.store.getAt(index);
23919 //console.log('selectRow - record id :' + r.id);
23921 this.selections.add(r);
23922 this.last = this.lastActive = index;
23923 if(!preventViewNotify){
23924 var proxy = new Roo.Element(
23925 this.grid.getRowDom(index)
23927 proxy.addClass('bg-info info');
23929 this.fireEvent("rowselect", this, index, r);
23930 this.fireEvent("selectionchange", this);
23936 * @param {Number} row The index of the row to deselect
23938 deselectRow : function(index, preventViewNotify)
23943 if(this.last == index){
23946 if(this.lastActive == index){
23947 this.lastActive = false;
23950 var r = this.grid.store.getAt(index);
23955 this.selections.remove(r);
23956 //.console.log('deselectRow - record id :' + r.id);
23957 if(!preventViewNotify){
23959 var proxy = new Roo.Element(
23960 this.grid.getRowDom(index)
23962 proxy.removeClass('bg-info info');
23964 this.fireEvent("rowdeselect", this, index);
23965 this.fireEvent("selectionchange", this);
23969 restoreLast : function(){
23971 this.last = this._last;
23976 acceptsNav : function(row, col, cm){
23977 return !cm.isHidden(col) && cm.isCellEditable(col, row);
23981 onEditorKey : function(field, e){
23982 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
23987 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
23989 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
23991 }else if(k == e.ENTER && !e.ctrlKey){
23995 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
23997 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
23999 }else if(k == e.ESC){
24003 g.startEditing(newCell[0], newCell[1]);
24009 * Ext JS Library 1.1.1
24010 * Copyright(c) 2006-2007, Ext JS, LLC.
24012 * Originally Released Under LGPL - original licence link has changed is not relivant.
24015 * <script type="text/javascript">
24019 * @class Roo.bootstrap.PagingToolbar
24020 * @extends Roo.bootstrap.NavSimplebar
24021 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24023 * Create a new PagingToolbar
24024 * @param {Object} config The config object
24025 * @param {Roo.data.Store} store
24027 Roo.bootstrap.PagingToolbar = function(config)
24029 // old args format still supported... - xtype is prefered..
24030 // created from xtype...
24032 this.ds = config.dataSource;
24034 if (config.store && !this.ds) {
24035 this.store= Roo.factory(config.store, Roo.data);
24036 this.ds = this.store;
24037 this.ds.xmodule = this.xmodule || false;
24040 this.toolbarItems = [];
24041 if (config.items) {
24042 this.toolbarItems = config.items;
24045 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24050 this.bind(this.ds);
24053 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24057 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24059 * @cfg {Roo.data.Store} dataSource
24060 * The underlying data store providing the paged data
24063 * @cfg {String/HTMLElement/Element} container
24064 * container The id or element that will contain the toolbar
24067 * @cfg {Boolean} displayInfo
24068 * True to display the displayMsg (defaults to false)
24071 * @cfg {Number} pageSize
24072 * The number of records to display per page (defaults to 20)
24076 * @cfg {String} displayMsg
24077 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24079 displayMsg : 'Displaying {0} - {1} of {2}',
24081 * @cfg {String} emptyMsg
24082 * The message to display when no records are found (defaults to "No data to display")
24084 emptyMsg : 'No data to display',
24086 * Customizable piece of the default paging text (defaults to "Page")
24089 beforePageText : "Page",
24091 * Customizable piece of the default paging text (defaults to "of %0")
24094 afterPageText : "of {0}",
24096 * Customizable piece of the default paging text (defaults to "First Page")
24099 firstText : "First Page",
24101 * Customizable piece of the default paging text (defaults to "Previous Page")
24104 prevText : "Previous Page",
24106 * Customizable piece of the default paging text (defaults to "Next Page")
24109 nextText : "Next Page",
24111 * Customizable piece of the default paging text (defaults to "Last Page")
24114 lastText : "Last Page",
24116 * Customizable piece of the default paging text (defaults to "Refresh")
24119 refreshText : "Refresh",
24123 onRender : function(ct, position)
24125 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24126 this.navgroup.parentId = this.id;
24127 this.navgroup.onRender(this.el, null);
24128 // add the buttons to the navgroup
24130 if(this.displayInfo){
24131 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24132 this.displayEl = this.el.select('.x-paging-info', true).first();
24133 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24134 // this.displayEl = navel.el.select('span',true).first();
24140 Roo.each(_this.buttons, function(e){ // this might need to use render????
24141 Roo.factory(e).onRender(_this.el, null);
24145 Roo.each(_this.toolbarItems, function(e) {
24146 _this.navgroup.addItem(e);
24150 this.first = this.navgroup.addItem({
24151 tooltip: this.firstText,
24153 icon : 'fa fa-backward',
24155 preventDefault: true,
24156 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24159 this.prev = this.navgroup.addItem({
24160 tooltip: this.prevText,
24162 icon : 'fa fa-step-backward',
24164 preventDefault: true,
24165 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
24167 //this.addSeparator();
24170 var field = this.navgroup.addItem( {
24172 cls : 'x-paging-position',
24174 html : this.beforePageText +
24175 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24176 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
24179 this.field = field.el.select('input', true).first();
24180 this.field.on("keydown", this.onPagingKeydown, this);
24181 this.field.on("focus", function(){this.dom.select();});
24184 this.afterTextEl = field.el.select('.x-paging-after',true).first();
24185 //this.field.setHeight(18);
24186 //this.addSeparator();
24187 this.next = this.navgroup.addItem({
24188 tooltip: this.nextText,
24190 html : ' <i class="fa fa-step-forward">',
24192 preventDefault: true,
24193 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
24195 this.last = this.navgroup.addItem({
24196 tooltip: this.lastText,
24197 icon : 'fa fa-forward',
24200 preventDefault: true,
24201 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
24203 //this.addSeparator();
24204 this.loading = this.navgroup.addItem({
24205 tooltip: this.refreshText,
24206 icon: 'fa fa-refresh',
24207 preventDefault: true,
24208 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24214 updateInfo : function(){
24215 if(this.displayEl){
24216 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24217 var msg = count == 0 ?
24221 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
24223 this.displayEl.update(msg);
24228 onLoad : function(ds, r, o)
24230 this.cursor = o.params ? o.params.start : 0;
24231 var d = this.getPageData(),
24236 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24237 this.field.dom.value = ap;
24238 this.first.setDisabled(ap == 1);
24239 this.prev.setDisabled(ap == 1);
24240 this.next.setDisabled(ap == ps);
24241 this.last.setDisabled(ap == ps);
24242 this.loading.enable();
24247 getPageData : function(){
24248 var total = this.ds.getTotalCount();
24251 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24252 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24257 onLoadError : function(){
24258 this.loading.enable();
24262 onPagingKeydown : function(e){
24263 var k = e.getKey();
24264 var d = this.getPageData();
24266 var v = this.field.dom.value, pageNum;
24267 if(!v || isNaN(pageNum = parseInt(v, 10))){
24268 this.field.dom.value = d.activePage;
24271 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24272 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24275 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))
24277 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24278 this.field.dom.value = pageNum;
24279 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24282 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24284 var v = this.field.dom.value, pageNum;
24285 var increment = (e.shiftKey) ? 10 : 1;
24286 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24289 if(!v || isNaN(pageNum = parseInt(v, 10))) {
24290 this.field.dom.value = d.activePage;
24293 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24295 this.field.dom.value = parseInt(v, 10) + increment;
24296 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24297 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24304 beforeLoad : function(){
24306 this.loading.disable();
24311 onClick : function(which){
24320 ds.load({params:{start: 0, limit: this.pageSize}});
24323 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24326 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24329 var total = ds.getTotalCount();
24330 var extra = total % this.pageSize;
24331 var lastStart = extra ? (total - extra) : total-this.pageSize;
24332 ds.load({params:{start: lastStart, limit: this.pageSize}});
24335 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24341 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24342 * @param {Roo.data.Store} store The data store to unbind
24344 unbind : function(ds){
24345 ds.un("beforeload", this.beforeLoad, this);
24346 ds.un("load", this.onLoad, this);
24347 ds.un("loadexception", this.onLoadError, this);
24348 ds.un("remove", this.updateInfo, this);
24349 ds.un("add", this.updateInfo, this);
24350 this.ds = undefined;
24354 * Binds the paging toolbar to the specified {@link Roo.data.Store}
24355 * @param {Roo.data.Store} store The data store to bind
24357 bind : function(ds){
24358 ds.on("beforeload", this.beforeLoad, this);
24359 ds.on("load", this.onLoad, this);
24360 ds.on("loadexception", this.onLoadError, this);
24361 ds.on("remove", this.updateInfo, this);
24362 ds.on("add", this.updateInfo, this);
24373 * @class Roo.bootstrap.MessageBar
24374 * @extends Roo.bootstrap.Component
24375 * Bootstrap MessageBar class
24376 * @cfg {String} html contents of the MessageBar
24377 * @cfg {String} weight (info | success | warning | danger) default info
24378 * @cfg {String} beforeClass insert the bar before the given class
24379 * @cfg {Boolean} closable (true | false) default false
24380 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24383 * Create a new Element
24384 * @param {Object} config The config object
24387 Roo.bootstrap.MessageBar = function(config){
24388 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24391 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
24397 beforeClass: 'bootstrap-sticky-wrap',
24399 getAutoCreate : function(){
24403 cls: 'alert alert-dismissable alert-' + this.weight,
24408 html: this.html || ''
24414 cfg.cls += ' alert-messages-fixed';
24428 onRender : function(ct, position)
24430 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24433 var cfg = Roo.apply({}, this.getAutoCreate());
24437 cfg.cls += ' ' + this.cls;
24440 cfg.style = this.style;
24442 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24444 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24447 this.el.select('>button.close').on('click', this.hide, this);
24453 if (!this.rendered) {
24459 this.fireEvent('show', this);
24465 if (!this.rendered) {
24471 this.fireEvent('hide', this);
24474 update : function()
24476 // var e = this.el.dom.firstChild;
24478 // if(this.closable){
24479 // e = e.nextSibling;
24482 // e.data = this.html || '';
24484 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24500 * @class Roo.bootstrap.Graph
24501 * @extends Roo.bootstrap.Component
24502 * Bootstrap Graph class
24506 @cfg {String} graphtype bar | vbar | pie
24507 @cfg {number} g_x coodinator | centre x (pie)
24508 @cfg {number} g_y coodinator | centre y (pie)
24509 @cfg {number} g_r radius (pie)
24510 @cfg {number} g_height height of the chart (respected by all elements in the set)
24511 @cfg {number} g_width width of the chart (respected by all elements in the set)
24512 @cfg {Object} title The title of the chart
24515 -opts (object) options for the chart
24517 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24518 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24520 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.
24521 o stacked (boolean) whether or not to tread values as in a stacked bar chart
24523 o stretch (boolean)
24525 -opts (object) options for the pie
24528 o startAngle (number)
24529 o endAngle (number)
24533 * Create a new Input
24534 * @param {Object} config The config object
24537 Roo.bootstrap.Graph = function(config){
24538 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24544 * The img click event for the img.
24545 * @param {Roo.EventObject} e
24551 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
24562 //g_colors: this.colors,
24569 getAutoCreate : function(){
24580 onRender : function(ct,position){
24583 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
24585 if (typeof(Raphael) == 'undefined') {
24586 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
24590 this.raphael = Raphael(this.el.dom);
24592 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24593 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24594 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24595 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
24597 r.text(160, 10, "Single Series Chart").attr(txtattr);
24598 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
24599 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
24600 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
24602 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
24603 r.barchart(330, 10, 300, 220, data1);
24604 r.barchart(10, 250, 300, 220, data2, {stacked: true});
24605 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
24608 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24609 // r.barchart(30, 30, 560, 250, xdata, {
24610 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
24611 // axis : "0 0 1 1",
24612 // axisxlabels : xdata
24613 // //yvalues : cols,
24616 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24618 // this.load(null,xdata,{
24619 // axis : "0 0 1 1",
24620 // axisxlabels : xdata
24625 load : function(graphtype,xdata,opts)
24627 this.raphael.clear();
24629 graphtype = this.graphtype;
24634 var r = this.raphael,
24635 fin = function () {
24636 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
24638 fout = function () {
24639 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
24641 pfin = function() {
24642 this.sector.stop();
24643 this.sector.scale(1.1, 1.1, this.cx, this.cy);
24646 this.label[0].stop();
24647 this.label[0].attr({ r: 7.5 });
24648 this.label[1].attr({ "font-weight": 800 });
24651 pfout = function() {
24652 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
24655 this.label[0].animate({ r: 5 }, 500, "bounce");
24656 this.label[1].attr({ "font-weight": 400 });
24662 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24665 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24668 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
24669 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
24671 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
24678 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
24683 setTitle: function(o)
24688 initEvents: function() {
24691 this.el.on('click', this.onClick, this);
24695 onClick : function(e)
24697 Roo.log('img onclick');
24698 this.fireEvent('click', this, e);
24710 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24713 * @class Roo.bootstrap.dash.NumberBox
24714 * @extends Roo.bootstrap.Component
24715 * Bootstrap NumberBox class
24716 * @cfg {String} headline Box headline
24717 * @cfg {String} content Box content
24718 * @cfg {String} icon Box icon
24719 * @cfg {String} footer Footer text
24720 * @cfg {String} fhref Footer href
24723 * Create a new NumberBox
24724 * @param {Object} config The config object
24728 Roo.bootstrap.dash.NumberBox = function(config){
24729 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
24733 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
24742 getAutoCreate : function(){
24746 cls : 'small-box ',
24754 cls : 'roo-headline',
24755 html : this.headline
24759 cls : 'roo-content',
24760 html : this.content
24774 cls : 'ion ' + this.icon
24783 cls : 'small-box-footer',
24784 href : this.fhref || '#',
24788 cfg.cn.push(footer);
24795 onRender : function(ct,position){
24796 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
24803 setHeadline: function (value)
24805 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
24808 setFooter: function (value, href)
24810 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
24813 this.el.select('a.small-box-footer',true).first().attr('href', href);
24818 setContent: function (value)
24820 this.el.select('.roo-content',true).first().dom.innerHTML = value;
24823 initEvents: function()
24837 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24840 * @class Roo.bootstrap.dash.TabBox
24841 * @extends Roo.bootstrap.Component
24842 * Bootstrap TabBox class
24843 * @cfg {String} title Title of the TabBox
24844 * @cfg {String} icon Icon of the TabBox
24845 * @cfg {Boolean} showtabs (true|false) show the tabs default true
24846 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
24849 * Create a new TabBox
24850 * @param {Object} config The config object
24854 Roo.bootstrap.dash.TabBox = function(config){
24855 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
24860 * When a pane is added
24861 * @param {Roo.bootstrap.dash.TabPane} pane
24865 * @event activatepane
24866 * When a pane is activated
24867 * @param {Roo.bootstrap.dash.TabPane} pane
24869 "activatepane" : true
24877 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
24882 tabScrollable : false,
24884 getChildContainer : function()
24886 return this.el.select('.tab-content', true).first();
24889 getAutoCreate : function(){
24893 cls: 'pull-left header',
24901 cls: 'fa ' + this.icon
24907 cls: 'nav nav-tabs pull-right',
24913 if(this.tabScrollable){
24920 cls: 'nav nav-tabs pull-right',
24931 cls: 'nav-tabs-custom',
24936 cls: 'tab-content no-padding',
24944 initEvents : function()
24946 //Roo.log('add add pane handler');
24947 this.on('addpane', this.onAddPane, this);
24950 * Updates the box title
24951 * @param {String} html to set the title to.
24953 setTitle : function(value)
24955 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
24957 onAddPane : function(pane)
24959 this.panes.push(pane);
24960 //Roo.log('addpane');
24962 // tabs are rendere left to right..
24963 if(!this.showtabs){
24967 var ctr = this.el.select('.nav-tabs', true).first();
24970 var existing = ctr.select('.nav-tab',true);
24971 var qty = existing.getCount();;
24974 var tab = ctr.createChild({
24976 cls : 'nav-tab' + (qty ? '' : ' active'),
24984 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
24987 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
24989 pane.el.addClass('active');
24994 onTabClick : function(ev,un,ob,pane)
24996 //Roo.log('tab - prev default');
24997 ev.preventDefault();
25000 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25001 pane.tab.addClass('active');
25002 //Roo.log(pane.title);
25003 this.getChildContainer().select('.tab-pane',true).removeClass('active');
25004 // technically we should have a deactivate event.. but maybe add later.
25005 // and it should not de-activate the selected tab...
25006 this.fireEvent('activatepane', pane);
25007 pane.el.addClass('active');
25008 pane.fireEvent('activate');
25013 getActivePane : function()
25016 Roo.each(this.panes, function(p) {
25017 if(p.el.hasClass('active')){
25038 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25040 * @class Roo.bootstrap.TabPane
25041 * @extends Roo.bootstrap.Component
25042 * Bootstrap TabPane class
25043 * @cfg {Boolean} active (false | true) Default false
25044 * @cfg {String} title title of panel
25048 * Create a new TabPane
25049 * @param {Object} config The config object
25052 Roo.bootstrap.dash.TabPane = function(config){
25053 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25059 * When a pane is activated
25060 * @param {Roo.bootstrap.dash.TabPane} pane
25067 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
25072 // the tabBox that this is attached to.
25075 getAutoCreate : function()
25083 cfg.cls += ' active';
25088 initEvents : function()
25090 //Roo.log('trigger add pane handler');
25091 this.parent().fireEvent('addpane', this)
25095 * Updates the tab title
25096 * @param {String} html to set the title to.
25098 setTitle: function(str)
25104 this.tab.select('a', true).first().dom.innerHTML = str;
25121 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25124 * @class Roo.bootstrap.menu.Menu
25125 * @extends Roo.bootstrap.Component
25126 * Bootstrap Menu class - container for Menu
25127 * @cfg {String} html Text of the menu
25128 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25129 * @cfg {String} icon Font awesome icon
25130 * @cfg {String} pos Menu align to (top | bottom) default bottom
25134 * Create a new Menu
25135 * @param {Object} config The config object
25139 Roo.bootstrap.menu.Menu = function(config){
25140 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25144 * @event beforeshow
25145 * Fires before this menu is displayed
25146 * @param {Roo.bootstrap.menu.Menu} this
25150 * @event beforehide
25151 * Fires before this menu is hidden
25152 * @param {Roo.bootstrap.menu.Menu} this
25157 * Fires after this menu is displayed
25158 * @param {Roo.bootstrap.menu.Menu} this
25163 * Fires after this menu is hidden
25164 * @param {Roo.bootstrap.menu.Menu} this
25169 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25170 * @param {Roo.bootstrap.menu.Menu} this
25171 * @param {Roo.EventObject} e
25178 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
25182 weight : 'default',
25187 getChildContainer : function() {
25188 if(this.isSubMenu){
25192 return this.el.select('ul.dropdown-menu', true).first();
25195 getAutoCreate : function()
25200 cls : 'roo-menu-text',
25208 cls : 'fa ' + this.icon
25219 cls : 'dropdown-button btn btn-' + this.weight,
25224 cls : 'dropdown-toggle btn btn-' + this.weight,
25234 cls : 'dropdown-menu'
25240 if(this.pos == 'top'){
25241 cfg.cls += ' dropup';
25244 if(this.isSubMenu){
25247 cls : 'dropdown-menu'
25254 onRender : function(ct, position)
25256 this.isSubMenu = ct.hasClass('dropdown-submenu');
25258 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25261 initEvents : function()
25263 if(this.isSubMenu){
25267 this.hidden = true;
25269 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25270 this.triggerEl.on('click', this.onTriggerPress, this);
25272 this.buttonEl = this.el.select('button.dropdown-button', true).first();
25273 this.buttonEl.on('click', this.onClick, this);
25279 if(this.isSubMenu){
25283 return this.el.select('ul.dropdown-menu', true).first();
25286 onClick : function(e)
25288 this.fireEvent("click", this, e);
25291 onTriggerPress : function(e)
25293 if (this.isVisible()) {
25300 isVisible : function(){
25301 return !this.hidden;
25306 this.fireEvent("beforeshow", this);
25308 this.hidden = false;
25309 this.el.addClass('open');
25311 Roo.get(document).on("mouseup", this.onMouseUp, this);
25313 this.fireEvent("show", this);
25320 this.fireEvent("beforehide", this);
25322 this.hidden = true;
25323 this.el.removeClass('open');
25325 Roo.get(document).un("mouseup", this.onMouseUp);
25327 this.fireEvent("hide", this);
25330 onMouseUp : function()
25344 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25347 * @class Roo.bootstrap.menu.Item
25348 * @extends Roo.bootstrap.Component
25349 * Bootstrap MenuItem class
25350 * @cfg {Boolean} submenu (true | false) default false
25351 * @cfg {String} html text of the item
25352 * @cfg {String} href the link
25353 * @cfg {Boolean} disable (true | false) default false
25354 * @cfg {Boolean} preventDefault (true | false) default true
25355 * @cfg {String} icon Font awesome icon
25356 * @cfg {String} pos Submenu align to (left | right) default right
25360 * Create a new Item
25361 * @param {Object} config The config object
25365 Roo.bootstrap.menu.Item = function(config){
25366 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25370 * Fires when the mouse is hovering over this menu
25371 * @param {Roo.bootstrap.menu.Item} this
25372 * @param {Roo.EventObject} e
25377 * Fires when the mouse exits this menu
25378 * @param {Roo.bootstrap.menu.Item} this
25379 * @param {Roo.EventObject} e
25385 * The raw click event for the entire grid.
25386 * @param {Roo.EventObject} e
25392 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
25397 preventDefault: true,
25402 getAutoCreate : function()
25407 cls : 'roo-menu-item-text',
25415 cls : 'fa ' + this.icon
25424 href : this.href || '#',
25431 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25435 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25437 if(this.pos == 'left'){
25438 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25445 initEvents : function()
25447 this.el.on('mouseover', this.onMouseOver, this);
25448 this.el.on('mouseout', this.onMouseOut, this);
25450 this.el.select('a', true).first().on('click', this.onClick, this);
25454 onClick : function(e)
25456 if(this.preventDefault){
25457 e.preventDefault();
25460 this.fireEvent("click", this, e);
25463 onMouseOver : function(e)
25465 if(this.submenu && this.pos == 'left'){
25466 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25469 this.fireEvent("mouseover", this, e);
25472 onMouseOut : function(e)
25474 this.fireEvent("mouseout", this, e);
25486 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25489 * @class Roo.bootstrap.menu.Separator
25490 * @extends Roo.bootstrap.Component
25491 * Bootstrap Separator class
25494 * Create a new Separator
25495 * @param {Object} config The config object
25499 Roo.bootstrap.menu.Separator = function(config){
25500 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25503 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
25505 getAutoCreate : function(){
25526 * @class Roo.bootstrap.Tooltip
25527 * Bootstrap Tooltip class
25528 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25529 * to determine which dom element triggers the tooltip.
25531 * It needs to add support for additional attributes like tooltip-position
25534 * Create a new Toolti
25535 * @param {Object} config The config object
25538 Roo.bootstrap.Tooltip = function(config){
25539 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25541 this.alignment = Roo.bootstrap.Tooltip.alignment;
25543 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25544 this.alignment = config.alignment;
25549 Roo.apply(Roo.bootstrap.Tooltip, {
25551 * @function init initialize tooltip monitoring.
25555 currentTip : false,
25556 currentRegion : false,
25562 Roo.get(document).on('mouseover', this.enter ,this);
25563 Roo.get(document).on('mouseout', this.leave, this);
25566 this.currentTip = new Roo.bootstrap.Tooltip();
25569 enter : function(ev)
25571 var dom = ev.getTarget();
25573 //Roo.log(['enter',dom]);
25574 var el = Roo.fly(dom);
25575 if (this.currentEl) {
25577 //Roo.log(this.currentEl);
25578 //Roo.log(this.currentEl.contains(dom));
25579 if (this.currentEl == el) {
25582 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
25588 if (this.currentTip.el) {
25589 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
25593 if(!el || el.dom == document){
25599 // you can not look for children, as if el is the body.. then everythign is the child..
25600 if (!el.attr('tooltip')) { //
25601 if (!el.select("[tooltip]").elements.length) {
25604 // is the mouse over this child...?
25605 bindEl = el.select("[tooltip]").first();
25606 var xy = ev.getXY();
25607 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
25608 //Roo.log("not in region.");
25611 //Roo.log("child element over..");
25614 this.currentEl = bindEl;
25615 this.currentTip.bind(bindEl);
25616 this.currentRegion = Roo.lib.Region.getRegion(dom);
25617 this.currentTip.enter();
25620 leave : function(ev)
25622 var dom = ev.getTarget();
25623 //Roo.log(['leave',dom]);
25624 if (!this.currentEl) {
25629 if (dom != this.currentEl.dom) {
25632 var xy = ev.getXY();
25633 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
25636 // only activate leave if mouse cursor is outside... bounding box..
25641 if (this.currentTip) {
25642 this.currentTip.leave();
25644 //Roo.log('clear currentEl');
25645 this.currentEl = false;
25650 'left' : ['r-l', [-2,0], 'right'],
25651 'right' : ['l-r', [2,0], 'left'],
25652 'bottom' : ['t-b', [0,2], 'top'],
25653 'top' : [ 'b-t', [0,-2], 'bottom']
25659 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
25664 delay : null, // can be { show : 300 , hide: 500}
25668 hoverState : null, //???
25670 placement : 'bottom',
25674 getAutoCreate : function(){
25681 cls : 'tooltip-arrow'
25684 cls : 'tooltip-inner'
25691 bind : function(el)
25697 enter : function () {
25699 if (this.timeout != null) {
25700 clearTimeout(this.timeout);
25703 this.hoverState = 'in';
25704 //Roo.log("enter - show");
25705 if (!this.delay || !this.delay.show) {
25710 this.timeout = setTimeout(function () {
25711 if (_t.hoverState == 'in') {
25714 }, this.delay.show);
25718 clearTimeout(this.timeout);
25720 this.hoverState = 'out';
25721 if (!this.delay || !this.delay.hide) {
25727 this.timeout = setTimeout(function () {
25728 //Roo.log("leave - timeout");
25730 if (_t.hoverState == 'out') {
25732 Roo.bootstrap.Tooltip.currentEl = false;
25737 show : function (msg)
25740 this.render(document.body);
25743 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
25745 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
25747 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
25749 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
25751 var placement = typeof this.placement == 'function' ?
25752 this.placement.call(this, this.el, on_el) :
25755 var autoToken = /\s?auto?\s?/i;
25756 var autoPlace = autoToken.test(placement);
25758 placement = placement.replace(autoToken, '') || 'top';
25762 //this.el.setXY([0,0]);
25764 //this.el.dom.style.display='block';
25766 //this.el.appendTo(on_el);
25768 var p = this.getPosition();
25769 var box = this.el.getBox();
25775 var align = this.alignment[placement];
25777 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
25779 if(placement == 'top' || placement == 'bottom'){
25781 placement = 'right';
25784 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
25785 placement = 'left';
25788 var scroll = Roo.select('body', true).first().getScroll();
25790 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
25796 this.el.alignTo(this.bindEl, align[0],align[1]);
25797 //var arrow = this.el.select('.arrow',true).first();
25798 //arrow.set(align[2],
25800 this.el.addClass(placement);
25802 this.el.addClass('in fade');
25804 this.hoverState = null;
25806 if (this.el.hasClass('fade')) {
25817 //this.el.setXY([0,0]);
25818 this.el.removeClass('in');
25834 * @class Roo.bootstrap.LocationPicker
25835 * @extends Roo.bootstrap.Component
25836 * Bootstrap LocationPicker class
25837 * @cfg {Number} latitude Position when init default 0
25838 * @cfg {Number} longitude Position when init default 0
25839 * @cfg {Number} zoom default 15
25840 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
25841 * @cfg {Boolean} mapTypeControl default false
25842 * @cfg {Boolean} disableDoubleClickZoom default false
25843 * @cfg {Boolean} scrollwheel default true
25844 * @cfg {Boolean} streetViewControl default false
25845 * @cfg {Number} radius default 0
25846 * @cfg {String} locationName
25847 * @cfg {Boolean} draggable default true
25848 * @cfg {Boolean} enableAutocomplete default false
25849 * @cfg {Boolean} enableReverseGeocode default true
25850 * @cfg {String} markerTitle
25853 * Create a new LocationPicker
25854 * @param {Object} config The config object
25858 Roo.bootstrap.LocationPicker = function(config){
25860 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
25865 * Fires when the picker initialized.
25866 * @param {Roo.bootstrap.LocationPicker} this
25867 * @param {Google Location} location
25871 * @event positionchanged
25872 * Fires when the picker position changed.
25873 * @param {Roo.bootstrap.LocationPicker} this
25874 * @param {Google Location} location
25876 positionchanged : true,
25879 * Fires when the map resize.
25880 * @param {Roo.bootstrap.LocationPicker} this
25885 * Fires when the map show.
25886 * @param {Roo.bootstrap.LocationPicker} this
25891 * Fires when the map hide.
25892 * @param {Roo.bootstrap.LocationPicker} this
25897 * Fires when click the map.
25898 * @param {Roo.bootstrap.LocationPicker} this
25899 * @param {Map event} e
25903 * @event mapRightClick
25904 * Fires when right click the map.
25905 * @param {Roo.bootstrap.LocationPicker} this
25906 * @param {Map event} e
25908 mapRightClick : true,
25910 * @event markerClick
25911 * Fires when click the marker.
25912 * @param {Roo.bootstrap.LocationPicker} this
25913 * @param {Map event} e
25915 markerClick : true,
25917 * @event markerRightClick
25918 * Fires when right click the marker.
25919 * @param {Roo.bootstrap.LocationPicker} this
25920 * @param {Map event} e
25922 markerRightClick : true,
25924 * @event OverlayViewDraw
25925 * Fires when OverlayView Draw
25926 * @param {Roo.bootstrap.LocationPicker} this
25928 OverlayViewDraw : true,
25930 * @event OverlayViewOnAdd
25931 * Fires when OverlayView Draw
25932 * @param {Roo.bootstrap.LocationPicker} this
25934 OverlayViewOnAdd : true,
25936 * @event OverlayViewOnRemove
25937 * Fires when OverlayView Draw
25938 * @param {Roo.bootstrap.LocationPicker} this
25940 OverlayViewOnRemove : true,
25942 * @event OverlayViewShow
25943 * Fires when OverlayView Draw
25944 * @param {Roo.bootstrap.LocationPicker} this
25945 * @param {Pixel} cpx
25947 OverlayViewShow : true,
25949 * @event OverlayViewHide
25950 * Fires when OverlayView Draw
25951 * @param {Roo.bootstrap.LocationPicker} this
25953 OverlayViewHide : true,
25955 * @event loadexception
25956 * Fires when load google lib failed.
25957 * @param {Roo.bootstrap.LocationPicker} this
25959 loadexception : true
25964 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
25966 gMapContext: false,
25972 mapTypeControl: false,
25973 disableDoubleClickZoom: false,
25975 streetViewControl: false,
25979 enableAutocomplete: false,
25980 enableReverseGeocode: true,
25983 getAutoCreate: function()
25988 cls: 'roo-location-picker'
25994 initEvents: function(ct, position)
25996 if(!this.el.getWidth() || this.isApplied()){
26000 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26005 initial: function()
26007 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26008 this.fireEvent('loadexception', this);
26012 if(!this.mapTypeId){
26013 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26016 this.gMapContext = this.GMapContext();
26018 this.initOverlayView();
26020 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26024 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26025 _this.setPosition(_this.gMapContext.marker.position);
26028 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26029 _this.fireEvent('mapClick', this, event);
26033 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26034 _this.fireEvent('mapRightClick', this, event);
26038 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26039 _this.fireEvent('markerClick', this, event);
26043 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26044 _this.fireEvent('markerRightClick', this, event);
26048 this.setPosition(this.gMapContext.location);
26050 this.fireEvent('initial', this, this.gMapContext.location);
26053 initOverlayView: function()
26057 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26061 _this.fireEvent('OverlayViewDraw', _this);
26066 _this.fireEvent('OverlayViewOnAdd', _this);
26069 onRemove: function()
26071 _this.fireEvent('OverlayViewOnRemove', _this);
26074 show: function(cpx)
26076 _this.fireEvent('OverlayViewShow', _this, cpx);
26081 _this.fireEvent('OverlayViewHide', _this);
26087 fromLatLngToContainerPixel: function(event)
26089 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26092 isApplied: function()
26094 return this.getGmapContext() == false ? false : true;
26097 getGmapContext: function()
26099 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26102 GMapContext: function()
26104 var position = new google.maps.LatLng(this.latitude, this.longitude);
26106 var _map = new google.maps.Map(this.el.dom, {
26109 mapTypeId: this.mapTypeId,
26110 mapTypeControl: this.mapTypeControl,
26111 disableDoubleClickZoom: this.disableDoubleClickZoom,
26112 scrollwheel: this.scrollwheel,
26113 streetViewControl: this.streetViewControl,
26114 locationName: this.locationName,
26115 draggable: this.draggable,
26116 enableAutocomplete: this.enableAutocomplete,
26117 enableReverseGeocode: this.enableReverseGeocode
26120 var _marker = new google.maps.Marker({
26121 position: position,
26123 title: this.markerTitle,
26124 draggable: this.draggable
26131 location: position,
26132 radius: this.radius,
26133 locationName: this.locationName,
26134 addressComponents: {
26135 formatted_address: null,
26136 addressLine1: null,
26137 addressLine2: null,
26139 streetNumber: null,
26143 stateOrProvince: null
26146 domContainer: this.el.dom,
26147 geodecoder: new google.maps.Geocoder()
26151 drawCircle: function(center, radius, options)
26153 if (this.gMapContext.circle != null) {
26154 this.gMapContext.circle.setMap(null);
26158 options = Roo.apply({}, options, {
26159 strokeColor: "#0000FF",
26160 strokeOpacity: .35,
26162 fillColor: "#0000FF",
26166 options.map = this.gMapContext.map;
26167 options.radius = radius;
26168 options.center = center;
26169 this.gMapContext.circle = new google.maps.Circle(options);
26170 return this.gMapContext.circle;
26176 setPosition: function(location)
26178 this.gMapContext.location = location;
26179 this.gMapContext.marker.setPosition(location);
26180 this.gMapContext.map.panTo(location);
26181 this.drawCircle(location, this.gMapContext.radius, {});
26185 if (this.gMapContext.settings.enableReverseGeocode) {
26186 this.gMapContext.geodecoder.geocode({
26187 latLng: this.gMapContext.location
26188 }, function(results, status) {
26190 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26191 _this.gMapContext.locationName = results[0].formatted_address;
26192 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26194 _this.fireEvent('positionchanged', this, location);
26201 this.fireEvent('positionchanged', this, location);
26206 google.maps.event.trigger(this.gMapContext.map, "resize");
26208 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26210 this.fireEvent('resize', this);
26213 setPositionByLatLng: function(latitude, longitude)
26215 this.setPosition(new google.maps.LatLng(latitude, longitude));
26218 getCurrentPosition: function()
26221 latitude: this.gMapContext.location.lat(),
26222 longitude: this.gMapContext.location.lng()
26226 getAddressName: function()
26228 return this.gMapContext.locationName;
26231 getAddressComponents: function()
26233 return this.gMapContext.addressComponents;
26236 address_component_from_google_geocode: function(address_components)
26240 for (var i = 0; i < address_components.length; i++) {
26241 var component = address_components[i];
26242 if (component.types.indexOf("postal_code") >= 0) {
26243 result.postalCode = component.short_name;
26244 } else if (component.types.indexOf("street_number") >= 0) {
26245 result.streetNumber = component.short_name;
26246 } else if (component.types.indexOf("route") >= 0) {
26247 result.streetName = component.short_name;
26248 } else if (component.types.indexOf("neighborhood") >= 0) {
26249 result.city = component.short_name;
26250 } else if (component.types.indexOf("locality") >= 0) {
26251 result.city = component.short_name;
26252 } else if (component.types.indexOf("sublocality") >= 0) {
26253 result.district = component.short_name;
26254 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26255 result.stateOrProvince = component.short_name;
26256 } else if (component.types.indexOf("country") >= 0) {
26257 result.country = component.short_name;
26261 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26262 result.addressLine2 = "";
26266 setZoomLevel: function(zoom)
26268 this.gMapContext.map.setZoom(zoom);
26281 this.fireEvent('show', this);
26292 this.fireEvent('hide', this);
26297 Roo.apply(Roo.bootstrap.LocationPicker, {
26299 OverlayView : function(map, options)
26301 options = options || {};
26315 * @class Roo.bootstrap.Alert
26316 * @extends Roo.bootstrap.Component
26317 * Bootstrap Alert class
26318 * @cfg {String} title The title of alert
26319 * @cfg {String} html The content of alert
26320 * @cfg {String} weight ( success | info | warning | danger )
26321 * @cfg {String} faicon font-awesomeicon
26324 * Create a new alert
26325 * @param {Object} config The config object
26329 Roo.bootstrap.Alert = function(config){
26330 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26334 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
26341 getAutoCreate : function()
26350 cls : 'roo-alert-icon'
26355 cls : 'roo-alert-title',
26360 cls : 'roo-alert-text',
26367 cfg.cn[0].cls += ' fa ' + this.faicon;
26371 cfg.cls += ' alert-' + this.weight;
26377 initEvents: function()
26379 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26382 setTitle : function(str)
26384 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26387 setText : function(str)
26389 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26392 setWeight : function(weight)
26395 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26398 this.weight = weight;
26400 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26403 setIcon : function(icon)
26406 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26409 this.faicon = icon;
26411 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26432 * @class Roo.bootstrap.UploadCropbox
26433 * @extends Roo.bootstrap.Component
26434 * Bootstrap UploadCropbox class
26435 * @cfg {String} emptyText show when image has been loaded
26436 * @cfg {String} rotateNotify show when image too small to rotate
26437 * @cfg {Number} errorTimeout default 3000
26438 * @cfg {Number} minWidth default 300
26439 * @cfg {Number} minHeight default 300
26440 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26441 * @cfg {Boolean} isDocument (true|false) default false
26442 * @cfg {String} url action url
26443 * @cfg {String} paramName default 'imageUpload'
26444 * @cfg {String} method default POST
26445 * @cfg {Boolean} loadMask (true|false) default true
26446 * @cfg {Boolean} loadingText default 'Loading...'
26449 * Create a new UploadCropbox
26450 * @param {Object} config The config object
26453 Roo.bootstrap.UploadCropbox = function(config){
26454 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26458 * @event beforeselectfile
26459 * Fire before select file
26460 * @param {Roo.bootstrap.UploadCropbox} this
26462 "beforeselectfile" : true,
26465 * Fire after initEvent
26466 * @param {Roo.bootstrap.UploadCropbox} this
26471 * Fire after initEvent
26472 * @param {Roo.bootstrap.UploadCropbox} this
26473 * @param {String} data
26478 * Fire when preparing the file data
26479 * @param {Roo.bootstrap.UploadCropbox} this
26480 * @param {Object} file
26485 * Fire when get exception
26486 * @param {Roo.bootstrap.UploadCropbox} this
26487 * @param {XMLHttpRequest} xhr
26489 "exception" : true,
26491 * @event beforeloadcanvas
26492 * Fire before load the canvas
26493 * @param {Roo.bootstrap.UploadCropbox} this
26494 * @param {String} src
26496 "beforeloadcanvas" : true,
26499 * Fire when trash image
26500 * @param {Roo.bootstrap.UploadCropbox} this
26505 * Fire when download the image
26506 * @param {Roo.bootstrap.UploadCropbox} this
26510 * @event footerbuttonclick
26511 * Fire when footerbuttonclick
26512 * @param {Roo.bootstrap.UploadCropbox} this
26513 * @param {String} type
26515 "footerbuttonclick" : true,
26519 * @param {Roo.bootstrap.UploadCropbox} this
26524 * Fire when rotate the image
26525 * @param {Roo.bootstrap.UploadCropbox} this
26526 * @param {String} pos
26531 * Fire when inspect the file
26532 * @param {Roo.bootstrap.UploadCropbox} this
26533 * @param {Object} file
26538 * Fire when xhr upload the file
26539 * @param {Roo.bootstrap.UploadCropbox} this
26540 * @param {Object} data
26545 * Fire when arrange the file data
26546 * @param {Roo.bootstrap.UploadCropbox} this
26547 * @param {Object} formData
26552 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26555 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
26557 emptyText : 'Click to upload image',
26558 rotateNotify : 'Image is too small to rotate',
26559 errorTimeout : 3000,
26573 cropType : 'image/jpeg',
26575 canvasLoaded : false,
26576 isDocument : false,
26578 paramName : 'imageUpload',
26580 loadingText : 'Loading...',
26583 getAutoCreate : function()
26587 cls : 'roo-upload-cropbox',
26591 cls : 'roo-upload-cropbox-selector',
26596 cls : 'roo-upload-cropbox-body',
26597 style : 'cursor:pointer',
26601 cls : 'roo-upload-cropbox-preview'
26605 cls : 'roo-upload-cropbox-thumb'
26609 cls : 'roo-upload-cropbox-empty-notify',
26610 html : this.emptyText
26614 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
26615 html : this.rotateNotify
26621 cls : 'roo-upload-cropbox-footer',
26624 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
26634 onRender : function(ct, position)
26636 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
26638 if (this.buttons.length) {
26640 Roo.each(this.buttons, function(bb) {
26642 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
26644 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
26650 this.maskEl = this.el;
26654 initEvents : function()
26656 this.urlAPI = (window.createObjectURL && window) ||
26657 (window.URL && URL.revokeObjectURL && URL) ||
26658 (window.webkitURL && webkitURL);
26660 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
26661 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26663 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
26664 this.selectorEl.hide();
26666 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
26667 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26669 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
26670 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26671 this.thumbEl.hide();
26673 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
26674 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26676 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
26677 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26678 this.errorEl.hide();
26680 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
26681 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26682 this.footerEl.hide();
26684 this.setThumbBoxSize();
26690 this.fireEvent('initial', this);
26697 window.addEventListener("resize", function() { _this.resize(); } );
26699 this.bodyEl.on('click', this.beforeSelectFile, this);
26702 this.bodyEl.on('touchstart', this.onTouchStart, this);
26703 this.bodyEl.on('touchmove', this.onTouchMove, this);
26704 this.bodyEl.on('touchend', this.onTouchEnd, this);
26708 this.bodyEl.on('mousedown', this.onMouseDown, this);
26709 this.bodyEl.on('mousemove', this.onMouseMove, this);
26710 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
26711 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
26712 Roo.get(document).on('mouseup', this.onMouseUp, this);
26715 this.selectorEl.on('change', this.onFileSelected, this);
26721 this.baseScale = 1;
26723 this.baseRotate = 1;
26724 this.dragable = false;
26725 this.pinching = false;
26728 this.cropData = false;
26729 this.notifyEl.dom.innerHTML = this.emptyText;
26731 this.selectorEl.dom.value = '';
26735 resize : function()
26737 if(this.fireEvent('resize', this) != false){
26738 this.setThumbBoxPosition();
26739 this.setCanvasPosition();
26743 onFooterButtonClick : function(e, el, o, type)
26746 case 'rotate-left' :
26747 this.onRotateLeft(e);
26749 case 'rotate-right' :
26750 this.onRotateRight(e);
26753 this.beforeSelectFile(e);
26768 this.fireEvent('footerbuttonclick', this, type);
26771 beforeSelectFile : function(e)
26773 e.preventDefault();
26775 if(this.fireEvent('beforeselectfile', this) != false){
26776 this.selectorEl.dom.click();
26780 onFileSelected : function(e)
26782 e.preventDefault();
26784 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26788 var file = this.selectorEl.dom.files[0];
26790 if(this.fireEvent('inspect', this, file) != false){
26791 this.prepare(file);
26796 trash : function(e)
26798 this.fireEvent('trash', this);
26801 download : function(e)
26803 this.fireEvent('download', this);
26806 loadCanvas : function(src)
26808 if(this.fireEvent('beforeloadcanvas', this, src) != false){
26812 this.imageEl = document.createElement('img');
26816 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
26818 this.imageEl.src = src;
26822 onLoadCanvas : function()
26824 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
26825 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
26827 this.bodyEl.un('click', this.beforeSelectFile, this);
26829 this.notifyEl.hide();
26830 this.thumbEl.show();
26831 this.footerEl.show();
26833 this.baseRotateLevel();
26835 if(this.isDocument){
26836 this.setThumbBoxSize();
26839 this.setThumbBoxPosition();
26841 this.baseScaleLevel();
26847 this.canvasLoaded = true;
26850 this.maskEl.unmask();
26855 setCanvasPosition : function()
26857 if(!this.canvasEl){
26861 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
26862 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
26864 this.previewEl.setLeft(pw);
26865 this.previewEl.setTop(ph);
26869 onMouseDown : function(e)
26873 this.dragable = true;
26874 this.pinching = false;
26876 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
26877 this.dragable = false;
26881 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26882 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26886 onMouseMove : function(e)
26890 if(!this.canvasLoaded){
26894 if (!this.dragable){
26898 var minX = Math.ceil(this.thumbEl.getLeft(true));
26899 var minY = Math.ceil(this.thumbEl.getTop(true));
26901 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
26902 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
26904 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26905 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26907 x = x - this.mouseX;
26908 y = y - this.mouseY;
26910 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
26911 var bgY = Math.ceil(y + this.previewEl.getTop(true));
26913 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
26914 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
26916 this.previewEl.setLeft(bgX);
26917 this.previewEl.setTop(bgY);
26919 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26920 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26923 onMouseUp : function(e)
26927 this.dragable = false;
26930 onMouseWheel : function(e)
26934 this.startScale = this.scale;
26936 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
26938 if(!this.zoomable()){
26939 this.scale = this.startScale;
26948 zoomable : function()
26950 var minScale = this.thumbEl.getWidth() / this.minWidth;
26952 if(this.minWidth < this.minHeight){
26953 minScale = this.thumbEl.getHeight() / this.minHeight;
26956 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
26957 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
26961 (this.rotate == 0 || this.rotate == 180) &&
26963 width > this.imageEl.OriginWidth ||
26964 height > this.imageEl.OriginHeight ||
26965 (width < this.minWidth && height < this.minHeight)
26973 (this.rotate == 90 || this.rotate == 270) &&
26975 width > this.imageEl.OriginWidth ||
26976 height > this.imageEl.OriginHeight ||
26977 (width < this.minHeight && height < this.minWidth)
26984 !this.isDocument &&
26985 (this.rotate == 0 || this.rotate == 180) &&
26987 width < this.minWidth ||
26988 width > this.imageEl.OriginWidth ||
26989 height < this.minHeight ||
26990 height > this.imageEl.OriginHeight
26997 !this.isDocument &&
26998 (this.rotate == 90 || this.rotate == 270) &&
27000 width < this.minHeight ||
27001 width > this.imageEl.OriginWidth ||
27002 height < this.minWidth ||
27003 height > this.imageEl.OriginHeight
27013 onRotateLeft : function(e)
27015 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27017 var minScale = this.thumbEl.getWidth() / this.minWidth;
27019 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27020 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27022 this.startScale = this.scale;
27024 while (this.getScaleLevel() < minScale){
27026 this.scale = this.scale + 1;
27028 if(!this.zoomable()){
27033 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27034 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27039 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27046 this.scale = this.startScale;
27048 this.onRotateFail();
27053 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27055 if(this.isDocument){
27056 this.setThumbBoxSize();
27057 this.setThumbBoxPosition();
27058 this.setCanvasPosition();
27063 this.fireEvent('rotate', this, 'left');
27067 onRotateRight : function(e)
27069 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27071 var minScale = this.thumbEl.getWidth() / this.minWidth;
27073 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27074 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27076 this.startScale = this.scale;
27078 while (this.getScaleLevel() < minScale){
27080 this.scale = this.scale + 1;
27082 if(!this.zoomable()){
27087 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27088 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27093 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27100 this.scale = this.startScale;
27102 this.onRotateFail();
27107 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27109 if(this.isDocument){
27110 this.setThumbBoxSize();
27111 this.setThumbBoxPosition();
27112 this.setCanvasPosition();
27117 this.fireEvent('rotate', this, 'right');
27120 onRotateFail : function()
27122 this.errorEl.show(true);
27126 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27131 this.previewEl.dom.innerHTML = '';
27133 var canvasEl = document.createElement("canvas");
27135 var contextEl = canvasEl.getContext("2d");
27137 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27138 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27139 var center = this.imageEl.OriginWidth / 2;
27141 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27142 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27143 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27144 center = this.imageEl.OriginHeight / 2;
27147 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27149 contextEl.translate(center, center);
27150 contextEl.rotate(this.rotate * Math.PI / 180);
27152 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27154 this.canvasEl = document.createElement("canvas");
27156 this.contextEl = this.canvasEl.getContext("2d");
27158 switch (this.rotate) {
27161 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27162 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27164 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27169 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27170 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27172 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27173 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);
27177 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27182 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27183 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27185 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27186 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);
27190 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);
27195 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27196 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27198 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27199 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27203 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);
27210 this.previewEl.appendChild(this.canvasEl);
27212 this.setCanvasPosition();
27217 if(!this.canvasLoaded){
27221 var imageCanvas = document.createElement("canvas");
27223 var imageContext = imageCanvas.getContext("2d");
27225 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27226 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27228 var center = imageCanvas.width / 2;
27230 imageContext.translate(center, center);
27232 imageContext.rotate(this.rotate * Math.PI / 180);
27234 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27236 var canvas = document.createElement("canvas");
27238 var context = canvas.getContext("2d");
27240 canvas.width = this.minWidth;
27241 canvas.height = this.minHeight;
27243 switch (this.rotate) {
27246 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27247 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27249 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27250 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27252 var targetWidth = this.minWidth - 2 * x;
27253 var targetHeight = this.minHeight - 2 * y;
27257 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27258 scale = targetWidth / width;
27261 if(x > 0 && y == 0){
27262 scale = targetHeight / height;
27265 if(x > 0 && y > 0){
27266 scale = targetWidth / width;
27268 if(width < height){
27269 scale = targetHeight / height;
27273 context.scale(scale, scale);
27275 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27276 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27278 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27279 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27281 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27286 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27287 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27289 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27290 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27292 var targetWidth = this.minWidth - 2 * x;
27293 var targetHeight = this.minHeight - 2 * y;
27297 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27298 scale = targetWidth / width;
27301 if(x > 0 && y == 0){
27302 scale = targetHeight / height;
27305 if(x > 0 && y > 0){
27306 scale = targetWidth / width;
27308 if(width < height){
27309 scale = targetHeight / height;
27313 context.scale(scale, scale);
27315 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27316 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27318 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27319 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27321 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27323 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27328 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27329 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27331 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27332 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27334 var targetWidth = this.minWidth - 2 * x;
27335 var targetHeight = this.minHeight - 2 * y;
27339 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27340 scale = targetWidth / width;
27343 if(x > 0 && y == 0){
27344 scale = targetHeight / height;
27347 if(x > 0 && y > 0){
27348 scale = targetWidth / width;
27350 if(width < height){
27351 scale = targetHeight / height;
27355 context.scale(scale, scale);
27357 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27358 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27360 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27361 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27363 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27364 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27366 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27371 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27372 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27374 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27375 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27377 var targetWidth = this.minWidth - 2 * x;
27378 var targetHeight = this.minHeight - 2 * y;
27382 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27383 scale = targetWidth / width;
27386 if(x > 0 && y == 0){
27387 scale = targetHeight / height;
27390 if(x > 0 && y > 0){
27391 scale = targetWidth / width;
27393 if(width < height){
27394 scale = targetHeight / height;
27398 context.scale(scale, scale);
27400 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27401 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27403 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27404 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27406 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27408 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27415 this.cropData = canvas.toDataURL(this.cropType);
27417 if(this.fireEvent('crop', this, this.cropData) !== false){
27418 this.process(this.file, this.cropData);
27425 setThumbBoxSize : function()
27429 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27430 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27431 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27433 this.minWidth = width;
27434 this.minHeight = height;
27436 if(this.rotate == 90 || this.rotate == 270){
27437 this.minWidth = height;
27438 this.minHeight = width;
27443 width = Math.ceil(this.minWidth * height / this.minHeight);
27445 if(this.minWidth > this.minHeight){
27447 height = Math.ceil(this.minHeight * width / this.minWidth);
27450 this.thumbEl.setStyle({
27451 width : width + 'px',
27452 height : height + 'px'
27459 setThumbBoxPosition : function()
27461 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27462 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27464 this.thumbEl.setLeft(x);
27465 this.thumbEl.setTop(y);
27469 baseRotateLevel : function()
27471 this.baseRotate = 1;
27474 typeof(this.exif) != 'undefined' &&
27475 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27476 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27478 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27481 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27485 baseScaleLevel : function()
27489 if(this.isDocument){
27491 if(this.baseRotate == 6 || this.baseRotate == 8){
27493 height = this.thumbEl.getHeight();
27494 this.baseScale = height / this.imageEl.OriginWidth;
27496 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27497 width = this.thumbEl.getWidth();
27498 this.baseScale = width / this.imageEl.OriginHeight;
27504 height = this.thumbEl.getHeight();
27505 this.baseScale = height / this.imageEl.OriginHeight;
27507 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27508 width = this.thumbEl.getWidth();
27509 this.baseScale = width / this.imageEl.OriginWidth;
27515 if(this.baseRotate == 6 || this.baseRotate == 8){
27517 width = this.thumbEl.getHeight();
27518 this.baseScale = width / this.imageEl.OriginHeight;
27520 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27521 height = this.thumbEl.getWidth();
27522 this.baseScale = height / this.imageEl.OriginHeight;
27525 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27526 height = this.thumbEl.getWidth();
27527 this.baseScale = height / this.imageEl.OriginHeight;
27529 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27530 width = this.thumbEl.getHeight();
27531 this.baseScale = width / this.imageEl.OriginWidth;
27538 width = this.thumbEl.getWidth();
27539 this.baseScale = width / this.imageEl.OriginWidth;
27541 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27542 height = this.thumbEl.getHeight();
27543 this.baseScale = height / this.imageEl.OriginHeight;
27546 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27548 height = this.thumbEl.getHeight();
27549 this.baseScale = height / this.imageEl.OriginHeight;
27551 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27552 width = this.thumbEl.getWidth();
27553 this.baseScale = width / this.imageEl.OriginWidth;
27561 getScaleLevel : function()
27563 return this.baseScale * Math.pow(1.1, this.scale);
27566 onTouchStart : function(e)
27568 if(!this.canvasLoaded){
27569 this.beforeSelectFile(e);
27573 var touches = e.browserEvent.touches;
27579 if(touches.length == 1){
27580 this.onMouseDown(e);
27584 if(touches.length != 2){
27590 for(var i = 0, finger; finger = touches[i]; i++){
27591 coords.push(finger.pageX, finger.pageY);
27594 var x = Math.pow(coords[0] - coords[2], 2);
27595 var y = Math.pow(coords[1] - coords[3], 2);
27597 this.startDistance = Math.sqrt(x + y);
27599 this.startScale = this.scale;
27601 this.pinching = true;
27602 this.dragable = false;
27606 onTouchMove : function(e)
27608 if(!this.pinching && !this.dragable){
27612 var touches = e.browserEvent.touches;
27619 this.onMouseMove(e);
27625 for(var i = 0, finger; finger = touches[i]; i++){
27626 coords.push(finger.pageX, finger.pageY);
27629 var x = Math.pow(coords[0] - coords[2], 2);
27630 var y = Math.pow(coords[1] - coords[3], 2);
27632 this.endDistance = Math.sqrt(x + y);
27634 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
27636 if(!this.zoomable()){
27637 this.scale = this.startScale;
27645 onTouchEnd : function(e)
27647 this.pinching = false;
27648 this.dragable = false;
27652 process : function(file, crop)
27655 this.maskEl.mask(this.loadingText);
27658 this.xhr = new XMLHttpRequest();
27660 file.xhr = this.xhr;
27662 this.xhr.open(this.method, this.url, true);
27665 "Accept": "application/json",
27666 "Cache-Control": "no-cache",
27667 "X-Requested-With": "XMLHttpRequest"
27670 for (var headerName in headers) {
27671 var headerValue = headers[headerName];
27673 this.xhr.setRequestHeader(headerName, headerValue);
27679 this.xhr.onload = function()
27681 _this.xhrOnLoad(_this.xhr);
27684 this.xhr.onerror = function()
27686 _this.xhrOnError(_this.xhr);
27689 var formData = new FormData();
27691 formData.append('returnHTML', 'NO');
27694 formData.append('crop', crop);
27697 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
27698 formData.append(this.paramName, file, file.name);
27701 if(typeof(file.filename) != 'undefined'){
27702 formData.append('filename', file.filename);
27705 if(typeof(file.mimetype) != 'undefined'){
27706 formData.append('mimetype', file.mimetype);
27709 if(this.fireEvent('arrange', this, formData) != false){
27710 this.xhr.send(formData);
27714 xhrOnLoad : function(xhr)
27717 this.maskEl.unmask();
27720 if (xhr.readyState !== 4) {
27721 this.fireEvent('exception', this, xhr);
27725 var response = Roo.decode(xhr.responseText);
27727 if(!response.success){
27728 this.fireEvent('exception', this, xhr);
27732 var response = Roo.decode(xhr.responseText);
27734 this.fireEvent('upload', this, response);
27738 xhrOnError : function()
27741 this.maskEl.unmask();
27744 Roo.log('xhr on error');
27746 var response = Roo.decode(xhr.responseText);
27752 prepare : function(file)
27755 this.maskEl.mask(this.loadingText);
27761 if(typeof(file) === 'string'){
27762 this.loadCanvas(file);
27766 if(!file || !this.urlAPI){
27771 this.cropType = file.type;
27775 if(this.fireEvent('prepare', this, this.file) != false){
27777 var reader = new FileReader();
27779 reader.onload = function (e) {
27780 if (e.target.error) {
27781 Roo.log(e.target.error);
27785 var buffer = e.target.result,
27786 dataView = new DataView(buffer),
27788 maxOffset = dataView.byteLength - 4,
27792 if (dataView.getUint16(0) === 0xffd8) {
27793 while (offset < maxOffset) {
27794 markerBytes = dataView.getUint16(offset);
27796 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
27797 markerLength = dataView.getUint16(offset + 2) + 2;
27798 if (offset + markerLength > dataView.byteLength) {
27799 Roo.log('Invalid meta data: Invalid segment size.');
27803 if(markerBytes == 0xffe1){
27804 _this.parseExifData(
27811 offset += markerLength;
27821 var url = _this.urlAPI.createObjectURL(_this.file);
27823 _this.loadCanvas(url);
27828 reader.readAsArrayBuffer(this.file);
27834 parseExifData : function(dataView, offset, length)
27836 var tiffOffset = offset + 10,
27840 if (dataView.getUint32(offset + 4) !== 0x45786966) {
27841 // No Exif data, might be XMP data instead
27845 // Check for the ASCII code for "Exif" (0x45786966):
27846 if (dataView.getUint32(offset + 4) !== 0x45786966) {
27847 // No Exif data, might be XMP data instead
27850 if (tiffOffset + 8 > dataView.byteLength) {
27851 Roo.log('Invalid Exif data: Invalid segment size.');
27854 // Check for the two null bytes:
27855 if (dataView.getUint16(offset + 8) !== 0x0000) {
27856 Roo.log('Invalid Exif data: Missing byte alignment offset.');
27859 // Check the byte alignment:
27860 switch (dataView.getUint16(tiffOffset)) {
27862 littleEndian = true;
27865 littleEndian = false;
27868 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
27871 // Check for the TIFF tag marker (0x002A):
27872 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
27873 Roo.log('Invalid Exif data: Missing TIFF marker.');
27876 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
27877 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
27879 this.parseExifTags(
27882 tiffOffset + dirOffset,
27887 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
27892 if (dirOffset + 6 > dataView.byteLength) {
27893 Roo.log('Invalid Exif data: Invalid directory offset.');
27896 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
27897 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
27898 if (dirEndOffset + 4 > dataView.byteLength) {
27899 Roo.log('Invalid Exif data: Invalid directory size.');
27902 for (i = 0; i < tagsNumber; i += 1) {
27906 dirOffset + 2 + 12 * i, // tag offset
27910 // Return the offset to the next directory:
27911 return dataView.getUint32(dirEndOffset, littleEndian);
27914 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
27916 var tag = dataView.getUint16(offset, littleEndian);
27918 this.exif[tag] = this.getExifValue(
27922 dataView.getUint16(offset + 2, littleEndian), // tag type
27923 dataView.getUint32(offset + 4, littleEndian), // tag length
27928 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
27930 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
27939 Roo.log('Invalid Exif data: Invalid tag type.');
27943 tagSize = tagType.size * length;
27944 // Determine if the value is contained in the dataOffset bytes,
27945 // or if the value at the dataOffset is a pointer to the actual data:
27946 dataOffset = tagSize > 4 ?
27947 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
27948 if (dataOffset + tagSize > dataView.byteLength) {
27949 Roo.log('Invalid Exif data: Invalid data offset.');
27952 if (length === 1) {
27953 return tagType.getValue(dataView, dataOffset, littleEndian);
27956 for (i = 0; i < length; i += 1) {
27957 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
27960 if (tagType.ascii) {
27962 // Concatenate the chars:
27963 for (i = 0; i < values.length; i += 1) {
27965 // Ignore the terminating NULL byte(s):
27966 if (c === '\u0000') {
27978 Roo.apply(Roo.bootstrap.UploadCropbox, {
27980 'Orientation': 0x0112
27984 1: 0, //'top-left',
27986 3: 180, //'bottom-right',
27987 // 4: 'bottom-left',
27989 6: 90, //'right-top',
27990 // 7: 'right-bottom',
27991 8: 270 //'left-bottom'
27995 // byte, 8-bit unsigned int:
27997 getValue: function (dataView, dataOffset) {
27998 return dataView.getUint8(dataOffset);
28002 // ascii, 8-bit byte:
28004 getValue: function (dataView, dataOffset) {
28005 return String.fromCharCode(dataView.getUint8(dataOffset));
28010 // short, 16 bit int:
28012 getValue: function (dataView, dataOffset, littleEndian) {
28013 return dataView.getUint16(dataOffset, littleEndian);
28017 // long, 32 bit int:
28019 getValue: function (dataView, dataOffset, littleEndian) {
28020 return dataView.getUint32(dataOffset, littleEndian);
28024 // rational = two long values, first is numerator, second is denominator:
28026 getValue: function (dataView, dataOffset, littleEndian) {
28027 return dataView.getUint32(dataOffset, littleEndian) /
28028 dataView.getUint32(dataOffset + 4, littleEndian);
28032 // slong, 32 bit signed int:
28034 getValue: function (dataView, dataOffset, littleEndian) {
28035 return dataView.getInt32(dataOffset, littleEndian);
28039 // srational, two slongs, first is numerator, second is denominator:
28041 getValue: function (dataView, dataOffset, littleEndian) {
28042 return dataView.getInt32(dataOffset, littleEndian) /
28043 dataView.getInt32(dataOffset + 4, littleEndian);
28053 cls : 'btn-group roo-upload-cropbox-rotate-left',
28054 action : 'rotate-left',
28058 cls : 'btn btn-default',
28059 html : '<i class="fa fa-undo"></i>'
28065 cls : 'btn-group roo-upload-cropbox-picture',
28066 action : 'picture',
28070 cls : 'btn btn-default',
28071 html : '<i class="fa fa-picture-o"></i>'
28077 cls : 'btn-group roo-upload-cropbox-rotate-right',
28078 action : 'rotate-right',
28082 cls : 'btn btn-default',
28083 html : '<i class="fa fa-repeat"></i>'
28091 cls : 'btn-group roo-upload-cropbox-rotate-left',
28092 action : 'rotate-left',
28096 cls : 'btn btn-default',
28097 html : '<i class="fa fa-undo"></i>'
28103 cls : 'btn-group roo-upload-cropbox-download',
28104 action : 'download',
28108 cls : 'btn btn-default',
28109 html : '<i class="fa fa-download"></i>'
28115 cls : 'btn-group roo-upload-cropbox-crop',
28120 cls : 'btn btn-default',
28121 html : '<i class="fa fa-crop"></i>'
28127 cls : 'btn-group roo-upload-cropbox-trash',
28132 cls : 'btn btn-default',
28133 html : '<i class="fa fa-trash"></i>'
28139 cls : 'btn-group roo-upload-cropbox-rotate-right',
28140 action : 'rotate-right',
28144 cls : 'btn btn-default',
28145 html : '<i class="fa fa-repeat"></i>'
28153 cls : 'btn-group roo-upload-cropbox-rotate-left',
28154 action : 'rotate-left',
28158 cls : 'btn btn-default',
28159 html : '<i class="fa fa-undo"></i>'
28165 cls : 'btn-group roo-upload-cropbox-rotate-right',
28166 action : 'rotate-right',
28170 cls : 'btn btn-default',
28171 html : '<i class="fa fa-repeat"></i>'
28184 * @class Roo.bootstrap.DocumentManager
28185 * @extends Roo.bootstrap.Component
28186 * Bootstrap DocumentManager class
28187 * @cfg {String} paramName default 'imageUpload'
28188 * @cfg {String} toolTipName default 'filename'
28189 * @cfg {String} method default POST
28190 * @cfg {String} url action url
28191 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28192 * @cfg {Boolean} multiple multiple upload default true
28193 * @cfg {Number} thumbSize default 300
28194 * @cfg {String} fieldLabel
28195 * @cfg {Number} labelWidth default 4
28196 * @cfg {String} labelAlign (left|top) default left
28197 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28198 * @cfg {Number} labellg set the width of label (1-12)
28199 * @cfg {Number} labelmd set the width of label (1-12)
28200 * @cfg {Number} labelsm set the width of label (1-12)
28201 * @cfg {Number} labelxs set the width of label (1-12)
28204 * Create a new DocumentManager
28205 * @param {Object} config The config object
28208 Roo.bootstrap.DocumentManager = function(config){
28209 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28212 this.delegates = [];
28217 * Fire when initial the DocumentManager
28218 * @param {Roo.bootstrap.DocumentManager} this
28223 * inspect selected file
28224 * @param {Roo.bootstrap.DocumentManager} this
28225 * @param {File} file
28230 * Fire when xhr load exception
28231 * @param {Roo.bootstrap.DocumentManager} this
28232 * @param {XMLHttpRequest} xhr
28234 "exception" : true,
28236 * @event afterupload
28237 * Fire when xhr load exception
28238 * @param {Roo.bootstrap.DocumentManager} this
28239 * @param {XMLHttpRequest} xhr
28241 "afterupload" : true,
28244 * prepare the form data
28245 * @param {Roo.bootstrap.DocumentManager} this
28246 * @param {Object} formData
28251 * Fire when remove the file
28252 * @param {Roo.bootstrap.DocumentManager} this
28253 * @param {Object} file
28258 * Fire after refresh the file
28259 * @param {Roo.bootstrap.DocumentManager} this
28264 * Fire after click the image
28265 * @param {Roo.bootstrap.DocumentManager} this
28266 * @param {Object} file
28271 * Fire when upload a image and editable set to true
28272 * @param {Roo.bootstrap.DocumentManager} this
28273 * @param {Object} file
28277 * @event beforeselectfile
28278 * Fire before select file
28279 * @param {Roo.bootstrap.DocumentManager} this
28281 "beforeselectfile" : true,
28284 * Fire before process file
28285 * @param {Roo.bootstrap.DocumentManager} this
28286 * @param {Object} file
28293 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
28302 paramName : 'imageUpload',
28303 toolTipName : 'filename',
28306 labelAlign : 'left',
28316 getAutoCreate : function()
28318 var managerWidget = {
28320 cls : 'roo-document-manager',
28324 cls : 'roo-document-manager-selector',
28329 cls : 'roo-document-manager-uploader',
28333 cls : 'roo-document-manager-upload-btn',
28334 html : '<i class="fa fa-plus"></i>'
28345 cls : 'column col-md-12',
28350 if(this.fieldLabel.length){
28355 cls : 'column col-md-12',
28356 html : this.fieldLabel
28360 cls : 'column col-md-12',
28365 if(this.labelAlign == 'left'){
28370 html : this.fieldLabel
28379 if(this.labelWidth > 12){
28380 content[0].style = "width: " + this.labelWidth + 'px';
28383 if(this.labelWidth < 13 && this.labelmd == 0){
28384 this.labelmd = this.labelWidth;
28387 if(this.labellg > 0){
28388 content[0].cls += ' col-lg-' + this.labellg;
28389 content[1].cls += ' col-lg-' + (12 - this.labellg);
28392 if(this.labelmd > 0){
28393 content[0].cls += ' col-md-' + this.labelmd;
28394 content[1].cls += ' col-md-' + (12 - this.labelmd);
28397 if(this.labelsm > 0){
28398 content[0].cls += ' col-sm-' + this.labelsm;
28399 content[1].cls += ' col-sm-' + (12 - this.labelsm);
28402 if(this.labelxs > 0){
28403 content[0].cls += ' col-xs-' + this.labelxs;
28404 content[1].cls += ' col-xs-' + (12 - this.labelxs);
28412 cls : 'row clearfix',
28420 initEvents : function()
28422 this.managerEl = this.el.select('.roo-document-manager', true).first();
28423 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28425 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28426 this.selectorEl.hide();
28429 this.selectorEl.attr('multiple', 'multiple');
28432 this.selectorEl.on('change', this.onFileSelected, this);
28434 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28435 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28437 this.uploader.on('click', this.onUploaderClick, this);
28439 this.renderProgressDialog();
28443 window.addEventListener("resize", function() { _this.refresh(); } );
28445 this.fireEvent('initial', this);
28448 renderProgressDialog : function()
28452 this.progressDialog = new Roo.bootstrap.Modal({
28453 cls : 'roo-document-manager-progress-dialog',
28454 allow_close : false,
28464 btnclick : function() {
28465 _this.uploadCancel();
28471 this.progressDialog.render(Roo.get(document.body));
28473 this.progress = new Roo.bootstrap.Progress({
28474 cls : 'roo-document-manager-progress',
28479 this.progress.render(this.progressDialog.getChildContainer());
28481 this.progressBar = new Roo.bootstrap.ProgressBar({
28482 cls : 'roo-document-manager-progress-bar',
28485 aria_valuemax : 12,
28489 this.progressBar.render(this.progress.getChildContainer());
28492 onUploaderClick : function(e)
28494 e.preventDefault();
28496 if(this.fireEvent('beforeselectfile', this) != false){
28497 this.selectorEl.dom.click();
28502 onFileSelected : function(e)
28504 e.preventDefault();
28506 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28510 Roo.each(this.selectorEl.dom.files, function(file){
28511 if(this.fireEvent('inspect', this, file) != false){
28512 this.files.push(file);
28522 this.selectorEl.dom.value = '';
28524 if(!this.files.length){
28528 if(this.boxes > 0 && this.files.length > this.boxes){
28529 this.files = this.files.slice(0, this.boxes);
28532 this.uploader.show();
28534 if(this.boxes > 0 && this.files.length > this.boxes - 1){
28535 this.uploader.hide();
28544 Roo.each(this.files, function(file){
28546 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28547 var f = this.renderPreview(file);
28552 if(file.type.indexOf('image') != -1){
28553 this.delegates.push(
28555 _this.process(file);
28556 }).createDelegate(this)
28564 _this.process(file);
28565 }).createDelegate(this)
28570 this.files = files;
28572 this.delegates = this.delegates.concat(docs);
28574 if(!this.delegates.length){
28579 this.progressBar.aria_valuemax = this.delegates.length;
28586 arrange : function()
28588 if(!this.delegates.length){
28589 this.progressDialog.hide();
28594 var delegate = this.delegates.shift();
28596 this.progressDialog.show();
28598 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
28600 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
28605 refresh : function()
28607 this.uploader.show();
28609 if(this.boxes > 0 && this.files.length > this.boxes - 1){
28610 this.uploader.hide();
28613 Roo.isTouch ? this.closable(false) : this.closable(true);
28615 this.fireEvent('refresh', this);
28618 onRemove : function(e, el, o)
28620 e.preventDefault();
28622 this.fireEvent('remove', this, o);
28626 remove : function(o)
28630 Roo.each(this.files, function(file){
28631 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
28640 this.files = files;
28647 Roo.each(this.files, function(file){
28652 file.target.remove();
28661 onClick : function(e, el, o)
28663 e.preventDefault();
28665 this.fireEvent('click', this, o);
28669 closable : function(closable)
28671 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
28673 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28685 xhrOnLoad : function(xhr)
28687 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28691 if (xhr.readyState !== 4) {
28693 this.fireEvent('exception', this, xhr);
28697 var response = Roo.decode(xhr.responseText);
28699 if(!response.success){
28701 this.fireEvent('exception', this, xhr);
28705 var file = this.renderPreview(response.data);
28707 this.files.push(file);
28711 this.fireEvent('afterupload', this, xhr);
28715 xhrOnError : function(xhr)
28717 Roo.log('xhr on error');
28719 var response = Roo.decode(xhr.responseText);
28726 process : function(file)
28728 if(this.fireEvent('process', this, file) !== false){
28729 if(this.editable && file.type.indexOf('image') != -1){
28730 this.fireEvent('edit', this, file);
28734 this.uploadStart(file, false);
28741 uploadStart : function(file, crop)
28743 this.xhr = new XMLHttpRequest();
28745 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28750 file.xhr = this.xhr;
28752 this.managerEl.createChild({
28754 cls : 'roo-document-manager-loading',
28758 tooltip : file.name,
28759 cls : 'roo-document-manager-thumb',
28760 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28766 this.xhr.open(this.method, this.url, true);
28769 "Accept": "application/json",
28770 "Cache-Control": "no-cache",
28771 "X-Requested-With": "XMLHttpRequest"
28774 for (var headerName in headers) {
28775 var headerValue = headers[headerName];
28777 this.xhr.setRequestHeader(headerName, headerValue);
28783 this.xhr.onload = function()
28785 _this.xhrOnLoad(_this.xhr);
28788 this.xhr.onerror = function()
28790 _this.xhrOnError(_this.xhr);
28793 var formData = new FormData();
28795 formData.append('returnHTML', 'NO');
28798 formData.append('crop', crop);
28801 formData.append(this.paramName, file, file.name);
28808 if(this.fireEvent('prepare', this, formData, options) != false){
28810 if(options.manually){
28814 this.xhr.send(formData);
28818 this.uploadCancel();
28821 uploadCancel : function()
28827 this.delegates = [];
28829 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28836 renderPreview : function(file)
28838 if(typeof(file.target) != 'undefined' && file.target){
28842 var previewEl = this.managerEl.createChild({
28844 cls : 'roo-document-manager-preview',
28848 tooltip : file[this.toolTipName],
28849 cls : 'roo-document-manager-thumb',
28850 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
28855 html : '<i class="fa fa-times-circle"></i>'
28860 var close = previewEl.select('button.close', true).first();
28862 close.on('click', this.onRemove, this, file);
28864 file.target = previewEl;
28866 var image = previewEl.select('img', true).first();
28870 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
28872 image.on('click', this.onClick, this, file);
28878 onPreviewLoad : function(file, image)
28880 if(typeof(file.target) == 'undefined' || !file.target){
28884 var width = image.dom.naturalWidth || image.dom.width;
28885 var height = image.dom.naturalHeight || image.dom.height;
28887 if(width > height){
28888 file.target.addClass('wide');
28892 file.target.addClass('tall');
28897 uploadFromSource : function(file, crop)
28899 this.xhr = new XMLHttpRequest();
28901 this.managerEl.createChild({
28903 cls : 'roo-document-manager-loading',
28907 tooltip : file.name,
28908 cls : 'roo-document-manager-thumb',
28909 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28915 this.xhr.open(this.method, this.url, true);
28918 "Accept": "application/json",
28919 "Cache-Control": "no-cache",
28920 "X-Requested-With": "XMLHttpRequest"
28923 for (var headerName in headers) {
28924 var headerValue = headers[headerName];
28926 this.xhr.setRequestHeader(headerName, headerValue);
28932 this.xhr.onload = function()
28934 _this.xhrOnLoad(_this.xhr);
28937 this.xhr.onerror = function()
28939 _this.xhrOnError(_this.xhr);
28942 var formData = new FormData();
28944 formData.append('returnHTML', 'NO');
28946 formData.append('crop', crop);
28948 if(typeof(file.filename) != 'undefined'){
28949 formData.append('filename', file.filename);
28952 if(typeof(file.mimetype) != 'undefined'){
28953 formData.append('mimetype', file.mimetype);
28958 if(this.fireEvent('prepare', this, formData) != false){
28959 this.xhr.send(formData);
28969 * @class Roo.bootstrap.DocumentViewer
28970 * @extends Roo.bootstrap.Component
28971 * Bootstrap DocumentViewer class
28972 * @cfg {Boolean} showDownload (true|false) show download button (default true)
28973 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
28976 * Create a new DocumentViewer
28977 * @param {Object} config The config object
28980 Roo.bootstrap.DocumentViewer = function(config){
28981 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
28986 * Fire after initEvent
28987 * @param {Roo.bootstrap.DocumentViewer} this
28993 * @param {Roo.bootstrap.DocumentViewer} this
28998 * Fire after download button
28999 * @param {Roo.bootstrap.DocumentViewer} this
29004 * Fire after trash button
29005 * @param {Roo.bootstrap.DocumentViewer} this
29012 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
29014 showDownload : true,
29018 getAutoCreate : function()
29022 cls : 'roo-document-viewer',
29026 cls : 'roo-document-viewer-body',
29030 cls : 'roo-document-viewer-thumb',
29034 cls : 'roo-document-viewer-image'
29042 cls : 'roo-document-viewer-footer',
29045 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29049 cls : 'btn-group roo-document-viewer-download',
29053 cls : 'btn btn-default',
29054 html : '<i class="fa fa-download"></i>'
29060 cls : 'btn-group roo-document-viewer-trash',
29064 cls : 'btn btn-default',
29065 html : '<i class="fa fa-trash"></i>'
29078 initEvents : function()
29080 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29081 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29083 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29084 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29086 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29087 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29089 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29090 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29092 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29093 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29095 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29096 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29098 this.bodyEl.on('click', this.onClick, this);
29099 this.downloadBtn.on('click', this.onDownload, this);
29100 this.trashBtn.on('click', this.onTrash, this);
29102 this.downloadBtn.hide();
29103 this.trashBtn.hide();
29105 if(this.showDownload){
29106 this.downloadBtn.show();
29109 if(this.showTrash){
29110 this.trashBtn.show();
29113 if(!this.showDownload && !this.showTrash) {
29114 this.footerEl.hide();
29119 initial : function()
29121 this.fireEvent('initial', this);
29125 onClick : function(e)
29127 e.preventDefault();
29129 this.fireEvent('click', this);
29132 onDownload : function(e)
29134 e.preventDefault();
29136 this.fireEvent('download', this);
29139 onTrash : function(e)
29141 e.preventDefault();
29143 this.fireEvent('trash', this);
29155 * @class Roo.bootstrap.NavProgressBar
29156 * @extends Roo.bootstrap.Component
29157 * Bootstrap NavProgressBar class
29160 * Create a new nav progress bar
29161 * @param {Object} config The config object
29164 Roo.bootstrap.NavProgressBar = function(config){
29165 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29167 this.bullets = this.bullets || [];
29169 // Roo.bootstrap.NavProgressBar.register(this);
29173 * Fires when the active item changes
29174 * @param {Roo.bootstrap.NavProgressBar} this
29175 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29176 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
29183 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
29188 getAutoCreate : function()
29190 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29194 cls : 'roo-navigation-bar-group',
29198 cls : 'roo-navigation-top-bar'
29202 cls : 'roo-navigation-bullets-bar',
29206 cls : 'roo-navigation-bar'
29213 cls : 'roo-navigation-bottom-bar'
29223 initEvents: function()
29228 onRender : function(ct, position)
29230 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29232 if(this.bullets.length){
29233 Roo.each(this.bullets, function(b){
29242 addItem : function(cfg)
29244 var item = new Roo.bootstrap.NavProgressItem(cfg);
29246 item.parentId = this.id;
29247 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29250 var top = new Roo.bootstrap.Element({
29252 cls : 'roo-navigation-bar-text'
29255 var bottom = new Roo.bootstrap.Element({
29257 cls : 'roo-navigation-bar-text'
29260 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29261 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29263 var topText = new Roo.bootstrap.Element({
29265 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29268 var bottomText = new Roo.bootstrap.Element({
29270 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29273 topText.onRender(top.el, null);
29274 bottomText.onRender(bottom.el, null);
29277 item.bottomEl = bottom;
29280 this.barItems.push(item);
29285 getActive : function()
29287 var active = false;
29289 Roo.each(this.barItems, function(v){
29291 if (!v.isActive()) {
29303 setActiveItem : function(item)
29307 Roo.each(this.barItems, function(v){
29308 if (v.rid == item.rid) {
29312 if (v.isActive()) {
29313 v.setActive(false);
29318 item.setActive(true);
29320 this.fireEvent('changed', this, item, prev);
29323 getBarItem: function(rid)
29327 Roo.each(this.barItems, function(e) {
29328 if (e.rid != rid) {
29339 indexOfItem : function(item)
29343 Roo.each(this.barItems, function(v, i){
29345 if (v.rid != item.rid) {
29356 setActiveNext : function()
29358 var i = this.indexOfItem(this.getActive());
29360 if (i > this.barItems.length) {
29364 this.setActiveItem(this.barItems[i+1]);
29367 setActivePrev : function()
29369 var i = this.indexOfItem(this.getActive());
29375 this.setActiveItem(this.barItems[i-1]);
29378 format : function()
29380 if(!this.barItems.length){
29384 var width = 100 / this.barItems.length;
29386 Roo.each(this.barItems, function(i){
29387 i.el.setStyle('width', width + '%');
29388 i.topEl.el.setStyle('width', width + '%');
29389 i.bottomEl.el.setStyle('width', width + '%');
29398 * Nav Progress Item
29403 * @class Roo.bootstrap.NavProgressItem
29404 * @extends Roo.bootstrap.Component
29405 * Bootstrap NavProgressItem class
29406 * @cfg {String} rid the reference id
29407 * @cfg {Boolean} active (true|false) Is item active default false
29408 * @cfg {Boolean} disabled (true|false) Is item active default false
29409 * @cfg {String} html
29410 * @cfg {String} position (top|bottom) text position default bottom
29411 * @cfg {String} icon show icon instead of number
29414 * Create a new NavProgressItem
29415 * @param {Object} config The config object
29417 Roo.bootstrap.NavProgressItem = function(config){
29418 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29423 * The raw click event for the entire grid.
29424 * @param {Roo.bootstrap.NavProgressItem} this
29425 * @param {Roo.EventObject} e
29432 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
29438 position : 'bottom',
29441 getAutoCreate : function()
29443 var iconCls = 'roo-navigation-bar-item-icon';
29445 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29449 cls: 'roo-navigation-bar-item',
29459 cfg.cls += ' active';
29462 cfg.cls += ' disabled';
29468 disable : function()
29470 this.setDisabled(true);
29473 enable : function()
29475 this.setDisabled(false);
29478 initEvents: function()
29480 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29482 this.iconEl.on('click', this.onClick, this);
29485 onClick : function(e)
29487 e.preventDefault();
29493 if(this.fireEvent('click', this, e) === false){
29497 this.parent().setActiveItem(this);
29500 isActive: function ()
29502 return this.active;
29505 setActive : function(state)
29507 if(this.active == state){
29511 this.active = state;
29514 this.el.addClass('active');
29518 this.el.removeClass('active');
29523 setDisabled : function(state)
29525 if(this.disabled == state){
29529 this.disabled = state;
29532 this.el.addClass('disabled');
29536 this.el.removeClass('disabled');
29539 tooltipEl : function()
29541 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29554 * @class Roo.bootstrap.FieldLabel
29555 * @extends Roo.bootstrap.Component
29556 * Bootstrap FieldLabel class
29557 * @cfg {String} html contents of the element
29558 * @cfg {String} tag tag of the element default label
29559 * @cfg {String} cls class of the element
29560 * @cfg {String} target label target
29561 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
29562 * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
29563 * @cfg {String} validClass default "text-success fa fa-lg fa-check"
29564 * @cfg {String} iconTooltip default "This field is required"
29567 * Create a new FieldLabel
29568 * @param {Object} config The config object
29571 Roo.bootstrap.FieldLabel = function(config){
29572 Roo.bootstrap.Element.superclass.constructor.call(this, config);
29577 * Fires after the field has been marked as invalid.
29578 * @param {Roo.form.FieldLabel} this
29579 * @param {String} msg The validation message
29584 * Fires after the field has been validated with no errors.
29585 * @param {Roo.form.FieldLabel} this
29591 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
29598 invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
29599 validClass : 'text-success fa fa-lg fa-check',
29600 iconTooltip : 'This field is required',
29602 getAutoCreate : function(){
29606 cls : 'roo-bootstrap-field-label ' + this.cls,
29612 tooltip : this.iconTooltip
29624 initEvents: function()
29626 Roo.bootstrap.Element.superclass.initEvents.call(this);
29628 this.iconEl = this.el.select('i', true).first();
29630 this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
29632 Roo.bootstrap.FieldLabel.register(this);
29636 * Mark this field as valid
29638 markValid : function()
29640 this.iconEl.show();
29642 this.iconEl.removeClass(this.invalidClass);
29644 this.iconEl.addClass(this.validClass);
29646 this.fireEvent('valid', this);
29650 * Mark this field as invalid
29651 * @param {String} msg The validation message
29653 markInvalid : function(msg)
29655 this.iconEl.show();
29657 this.iconEl.removeClass(this.validClass);
29659 this.iconEl.addClass(this.invalidClass);
29661 this.fireEvent('invalid', this, msg);
29667 Roo.apply(Roo.bootstrap.FieldLabel, {
29672 * register a FieldLabel Group
29673 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
29675 register : function(label)
29677 if(this.groups.hasOwnProperty(label.target)){
29681 this.groups[label.target] = label;
29685 * fetch a FieldLabel Group based on the target
29686 * @param {string} target
29687 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
29689 get: function(target) {
29690 if (typeof(this.groups[target]) == 'undefined') {
29694 return this.groups[target] ;
29703 * page DateSplitField.
29709 * @class Roo.bootstrap.DateSplitField
29710 * @extends Roo.bootstrap.Component
29711 * Bootstrap DateSplitField class
29712 * @cfg {string} fieldLabel - the label associated
29713 * @cfg {Number} labelWidth set the width of label (0-12)
29714 * @cfg {String} labelAlign (top|left)
29715 * @cfg {Boolean} dayAllowBlank (true|false) default false
29716 * @cfg {Boolean} monthAllowBlank (true|false) default false
29717 * @cfg {Boolean} yearAllowBlank (true|false) default false
29718 * @cfg {string} dayPlaceholder
29719 * @cfg {string} monthPlaceholder
29720 * @cfg {string} yearPlaceholder
29721 * @cfg {string} dayFormat default 'd'
29722 * @cfg {string} monthFormat default 'm'
29723 * @cfg {string} yearFormat default 'Y'
29724 * @cfg {Number} labellg set the width of label (1-12)
29725 * @cfg {Number} labelmd set the width of label (1-12)
29726 * @cfg {Number} labelsm set the width of label (1-12)
29727 * @cfg {Number} labelxs set the width of label (1-12)
29731 * Create a new DateSplitField
29732 * @param {Object} config The config object
29735 Roo.bootstrap.DateSplitField = function(config){
29736 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
29742 * getting the data of years
29743 * @param {Roo.bootstrap.DateSplitField} this
29744 * @param {Object} years
29749 * getting the data of days
29750 * @param {Roo.bootstrap.DateSplitField} this
29751 * @param {Object} days
29756 * Fires after the field has been marked as invalid.
29757 * @param {Roo.form.Field} this
29758 * @param {String} msg The validation message
29763 * Fires after the field has been validated with no errors.
29764 * @param {Roo.form.Field} this
29770 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
29773 labelAlign : 'top',
29775 dayAllowBlank : false,
29776 monthAllowBlank : false,
29777 yearAllowBlank : false,
29778 dayPlaceholder : '',
29779 monthPlaceholder : '',
29780 yearPlaceholder : '',
29784 isFormField : true,
29790 getAutoCreate : function()
29794 cls : 'row roo-date-split-field-group',
29799 cls : 'form-hidden-field roo-date-split-field-group-value',
29805 var labelCls = 'col-md-12';
29806 var contentCls = 'col-md-4';
29808 if(this.fieldLabel){
29812 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
29816 html : this.fieldLabel
29821 if(this.labelAlign == 'left'){
29823 if(this.labelWidth > 12){
29824 label.style = "width: " + this.labelWidth + 'px';
29827 if(this.labelWidth < 13 && this.labelmd == 0){
29828 this.labelmd = this.labelWidth;
29831 if(this.labellg > 0){
29832 labelCls = ' col-lg-' + this.labellg;
29833 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
29836 if(this.labelmd > 0){
29837 labelCls = ' col-md-' + this.labelmd;
29838 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
29841 if(this.labelsm > 0){
29842 labelCls = ' col-sm-' + this.labelsm;
29843 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
29846 if(this.labelxs > 0){
29847 labelCls = ' col-xs-' + this.labelxs;
29848 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
29852 label.cls += ' ' + labelCls;
29854 cfg.cn.push(label);
29857 Roo.each(['day', 'month', 'year'], function(t){
29860 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
29867 inputEl: function ()
29869 return this.el.select('.roo-date-split-field-group-value', true).first();
29872 onRender : function(ct, position)
29876 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29878 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
29880 this.dayField = new Roo.bootstrap.ComboBox({
29881 allowBlank : this.dayAllowBlank,
29882 alwaysQuery : true,
29883 displayField : 'value',
29886 forceSelection : true,
29888 placeholder : this.dayPlaceholder,
29889 selectOnFocus : true,
29890 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29891 triggerAction : 'all',
29893 valueField : 'value',
29894 store : new Roo.data.SimpleStore({
29895 data : (function() {
29897 _this.fireEvent('days', _this, days);
29900 fields : [ 'value' ]
29903 select : function (_self, record, index)
29905 _this.setValue(_this.getValue());
29910 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
29912 this.monthField = new Roo.bootstrap.MonthField({
29913 after : '<i class=\"fa fa-calendar\"></i>',
29914 allowBlank : this.monthAllowBlank,
29915 placeholder : this.monthPlaceholder,
29918 render : function (_self)
29920 this.el.select('span.input-group-addon', true).first().on('click', function(e){
29921 e.preventDefault();
29925 select : function (_self, oldvalue, newvalue)
29927 _this.setValue(_this.getValue());
29932 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
29934 this.yearField = new Roo.bootstrap.ComboBox({
29935 allowBlank : this.yearAllowBlank,
29936 alwaysQuery : true,
29937 displayField : 'value',
29940 forceSelection : true,
29942 placeholder : this.yearPlaceholder,
29943 selectOnFocus : true,
29944 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29945 triggerAction : 'all',
29947 valueField : 'value',
29948 store : new Roo.data.SimpleStore({
29949 data : (function() {
29951 _this.fireEvent('years', _this, years);
29954 fields : [ 'value' ]
29957 select : function (_self, record, index)
29959 _this.setValue(_this.getValue());
29964 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
29967 setValue : function(v, format)
29969 this.inputEl.dom.value = v;
29971 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
29973 var d = Date.parseDate(v, f);
29980 this.setDay(d.format(this.dayFormat));
29981 this.setMonth(d.format(this.monthFormat));
29982 this.setYear(d.format(this.yearFormat));
29989 setDay : function(v)
29991 this.dayField.setValue(v);
29992 this.inputEl.dom.value = this.getValue();
29997 setMonth : function(v)
29999 this.monthField.setValue(v, true);
30000 this.inputEl.dom.value = this.getValue();
30005 setYear : function(v)
30007 this.yearField.setValue(v);
30008 this.inputEl.dom.value = this.getValue();
30013 getDay : function()
30015 return this.dayField.getValue();
30018 getMonth : function()
30020 return this.monthField.getValue();
30023 getYear : function()
30025 return this.yearField.getValue();
30028 getValue : function()
30030 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30032 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30042 this.inputEl.dom.value = '';
30047 validate : function()
30049 var d = this.dayField.validate();
30050 var m = this.monthField.validate();
30051 var y = this.yearField.validate();
30056 (!this.dayAllowBlank && !d) ||
30057 (!this.monthAllowBlank && !m) ||
30058 (!this.yearAllowBlank && !y)
30063 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30072 this.markInvalid();
30077 markValid : function()
30080 var label = this.el.select('label', true).first();
30081 var icon = this.el.select('i.fa-star', true).first();
30087 this.fireEvent('valid', this);
30091 * Mark this field as invalid
30092 * @param {String} msg The validation message
30094 markInvalid : function(msg)
30097 var label = this.el.select('label', true).first();
30098 var icon = this.el.select('i.fa-star', true).first();
30100 if(label && !icon){
30101 this.el.select('.roo-date-split-field-label', true).createChild({
30103 cls : 'text-danger fa fa-lg fa-star',
30104 tooltip : 'This field is required',
30105 style : 'margin-right:5px;'
30109 this.fireEvent('invalid', this, msg);
30112 clearInvalid : function()
30114 var label = this.el.select('label', true).first();
30115 var icon = this.el.select('i.fa-star', true).first();
30121 this.fireEvent('valid', this);
30124 getName: function()
30134 * http://masonry.desandro.com
30136 * The idea is to render all the bricks based on vertical width...
30138 * The original code extends 'outlayer' - we might need to use that....
30144 * @class Roo.bootstrap.LayoutMasonry
30145 * @extends Roo.bootstrap.Component
30146 * Bootstrap Layout Masonry class
30149 * Create a new Element
30150 * @param {Object} config The config object
30153 Roo.bootstrap.LayoutMasonry = function(config){
30155 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30159 Roo.bootstrap.LayoutMasonry.register(this);
30165 * Fire after layout the items
30166 * @param {Roo.bootstrap.LayoutMasonry} this
30167 * @param {Roo.EventObject} e
30174 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
30177 * @cfg {Boolean} isLayoutInstant = no animation?
30179 isLayoutInstant : false, // needed?
30182 * @cfg {Number} boxWidth width of the columns
30187 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
30192 * @cfg {Number} padWidth padding below box..
30197 * @cfg {Number} gutter gutter width..
30202 * @cfg {Number} maxCols maximum number of columns
30208 * @cfg {Boolean} isAutoInitial defalut true
30210 isAutoInitial : true,
30215 * @cfg {Boolean} isHorizontal defalut false
30217 isHorizontal : false,
30219 currentSize : null,
30225 bricks: null, //CompositeElement
30229 _isLayoutInited : false,
30231 // isAlternative : false, // only use for vertical layout...
30234 * @cfg {Number} alternativePadWidth padding below box..
30236 alternativePadWidth : 50,
30238 selectedBrick : [],
30240 getAutoCreate : function(){
30242 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30246 cls: 'blog-masonary-wrapper ' + this.cls,
30248 cls : 'mas-boxes masonary'
30255 getChildContainer: function( )
30257 if (this.boxesEl) {
30258 return this.boxesEl;
30261 this.boxesEl = this.el.select('.mas-boxes').first();
30263 return this.boxesEl;
30267 initEvents : function()
30271 if(this.isAutoInitial){
30272 Roo.log('hook children rendered');
30273 this.on('childrenrendered', function() {
30274 Roo.log('children rendered');
30280 initial : function()
30282 this.selectedBrick = [];
30284 this.currentSize = this.el.getBox(true);
30286 Roo.EventManager.onWindowResize(this.resize, this);
30288 if(!this.isAutoInitial){
30296 //this.layout.defer(500,this);
30300 resize : function()
30302 var cs = this.el.getBox(true);
30305 this.currentSize.width == cs.width &&
30306 this.currentSize.x == cs.x &&
30307 this.currentSize.height == cs.height &&
30308 this.currentSize.y == cs.y
30310 Roo.log("no change in with or X or Y");
30314 this.currentSize = cs;
30320 layout : function()
30322 this._resetLayout();
30324 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30326 this.layoutItems( isInstant );
30328 this._isLayoutInited = true;
30330 this.fireEvent('layout', this);
30334 _resetLayout : function()
30336 if(this.isHorizontal){
30337 this.horizontalMeasureColumns();
30341 this.verticalMeasureColumns();
30345 verticalMeasureColumns : function()
30347 this.getContainerWidth();
30349 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30350 // this.colWidth = Math.floor(this.containerWidth * 0.8);
30354 var boxWidth = this.boxWidth + this.padWidth;
30356 if(this.containerWidth < this.boxWidth){
30357 boxWidth = this.containerWidth
30360 var containerWidth = this.containerWidth;
30362 var cols = Math.floor(containerWidth / boxWidth);
30364 this.cols = Math.max( cols, 1 );
30366 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30368 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30370 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30372 this.colWidth = boxWidth + avail - this.padWidth;
30374 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30375 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
30378 horizontalMeasureColumns : function()
30380 this.getContainerWidth();
30382 var boxWidth = this.boxWidth;
30384 if(this.containerWidth < boxWidth){
30385 boxWidth = this.containerWidth;
30388 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30390 this.el.setHeight(boxWidth);
30394 getContainerWidth : function()
30396 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
30399 layoutItems : function( isInstant )
30401 Roo.log(this.bricks);
30403 var items = Roo.apply([], this.bricks);
30405 if(this.isHorizontal){
30406 this._horizontalLayoutItems( items , isInstant );
30410 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30411 // this._verticalAlternativeLayoutItems( items , isInstant );
30415 this._verticalLayoutItems( items , isInstant );
30419 _verticalLayoutItems : function ( items , isInstant)
30421 if ( !items || !items.length ) {
30426 ['xs', 'xs', 'xs', 'tall'],
30427 ['xs', 'xs', 'tall'],
30428 ['xs', 'xs', 'sm'],
30429 ['xs', 'xs', 'xs'],
30435 ['sm', 'xs', 'xs'],
30439 ['tall', 'xs', 'xs', 'xs'],
30440 ['tall', 'xs', 'xs'],
30452 Roo.each(items, function(item, k){
30454 switch (item.size) {
30455 // these layouts take up a full box,
30466 boxes.push([item]);
30489 var filterPattern = function(box, length)
30497 var pattern = box.slice(0, length);
30501 Roo.each(pattern, function(i){
30502 format.push(i.size);
30505 Roo.each(standard, function(s){
30507 if(String(s) != String(format)){
30516 if(!match && length == 1){
30521 filterPattern(box, length - 1);
30525 queue.push(pattern);
30527 box = box.slice(length, box.length);
30529 filterPattern(box, 4);
30535 Roo.each(boxes, function(box, k){
30541 if(box.length == 1){
30546 filterPattern(box, 4);
30550 this._processVerticalLayoutQueue( queue, isInstant );
30554 // _verticalAlternativeLayoutItems : function( items , isInstant )
30556 // if ( !items || !items.length ) {
30560 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
30564 _horizontalLayoutItems : function ( items , isInstant)
30566 if ( !items || !items.length || items.length < 3) {
30572 var eItems = items.slice(0, 3);
30574 items = items.slice(3, items.length);
30577 ['xs', 'xs', 'xs', 'wide'],
30578 ['xs', 'xs', 'wide'],
30579 ['xs', 'xs', 'sm'],
30580 ['xs', 'xs', 'xs'],
30586 ['sm', 'xs', 'xs'],
30590 ['wide', 'xs', 'xs', 'xs'],
30591 ['wide', 'xs', 'xs'],
30604 Roo.each(items, function(item, k){
30606 switch (item.size) {
30617 boxes.push([item]);
30641 var filterPattern = function(box, length)
30649 var pattern = box.slice(0, length);
30653 Roo.each(pattern, function(i){
30654 format.push(i.size);
30657 Roo.each(standard, function(s){
30659 if(String(s) != String(format)){
30668 if(!match && length == 1){
30673 filterPattern(box, length - 1);
30677 queue.push(pattern);
30679 box = box.slice(length, box.length);
30681 filterPattern(box, 4);
30687 Roo.each(boxes, function(box, k){
30693 if(box.length == 1){
30698 filterPattern(box, 4);
30705 var pos = this.el.getBox(true);
30709 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30711 var hit_end = false;
30713 Roo.each(queue, function(box){
30717 Roo.each(box, function(b){
30719 b.el.setVisibilityMode(Roo.Element.DISPLAY);
30729 Roo.each(box, function(b){
30731 b.el.setVisibilityMode(Roo.Element.DISPLAY);
30734 mx = Math.max(mx, b.x);
30738 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
30742 Roo.each(box, function(b){
30744 b.el.setVisibilityMode(Roo.Element.DISPLAY);
30758 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
30761 /** Sets position of item in DOM
30762 * @param {Element} item
30763 * @param {Number} x - horizontal position
30764 * @param {Number} y - vertical position
30765 * @param {Boolean} isInstant - disables transitions
30767 _processVerticalLayoutQueue : function( queue, isInstant )
30769 var pos = this.el.getBox(true);
30774 for (var i = 0; i < this.cols; i++){
30778 Roo.each(queue, function(box, k){
30780 var col = k % this.cols;
30782 Roo.each(box, function(b,kk){
30784 b.el.position('absolute');
30786 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30787 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30789 if(b.size == 'md-left' || b.size == 'md-right'){
30790 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30791 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30794 b.el.setWidth(width);
30795 b.el.setHeight(height);
30797 b.el.select('iframe',true).setSize(width,height);
30801 for (var i = 0; i < this.cols; i++){
30803 if(maxY[i] < maxY[col]){
30808 col = Math.min(col, i);
30812 x = pos.x + col * (this.colWidth + this.padWidth);
30816 var positions = [];
30818 switch (box.length){
30820 positions = this.getVerticalOneBoxColPositions(x, y, box);
30823 positions = this.getVerticalTwoBoxColPositions(x, y, box);
30826 positions = this.getVerticalThreeBoxColPositions(x, y, box);
30829 positions = this.getVerticalFourBoxColPositions(x, y, box);
30835 Roo.each(box, function(b,kk){
30837 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30839 var sz = b.el.getSize();
30841 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
30849 for (var i = 0; i < this.cols; i++){
30850 mY = Math.max(mY, maxY[i]);
30853 this.el.setHeight(mY - pos.y);
30857 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
30859 // var pos = this.el.getBox(true);
30862 // var maxX = pos.right;
30864 // var maxHeight = 0;
30866 // Roo.each(items, function(item, k){
30870 // item.el.position('absolute');
30872 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
30874 // item.el.setWidth(width);
30876 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
30878 // item.el.setHeight(height);
30881 // item.el.setXY([x, y], isInstant ? false : true);
30883 // item.el.setXY([maxX - width, y], isInstant ? false : true);
30886 // y = y + height + this.alternativePadWidth;
30888 // maxHeight = maxHeight + height + this.alternativePadWidth;
30892 // this.el.setHeight(maxHeight);
30896 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
30898 var pos = this.el.getBox(true);
30903 var maxX = pos.right;
30905 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
30907 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30909 Roo.each(queue, function(box, k){
30911 Roo.each(box, function(b, kk){
30913 b.el.position('absolute');
30915 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30916 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30918 if(b.size == 'md-left' || b.size == 'md-right'){
30919 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30920 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30923 b.el.setWidth(width);
30924 b.el.setHeight(height);
30932 var positions = [];
30934 switch (box.length){
30936 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
30939 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
30942 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
30945 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
30951 Roo.each(box, function(b,kk){
30953 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30955 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
30963 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
30965 Roo.each(eItems, function(b,k){
30967 b.size = (k == 0) ? 'sm' : 'xs';
30968 b.x = (k == 0) ? 2 : 1;
30969 b.y = (k == 0) ? 2 : 1;
30971 b.el.position('absolute');
30973 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30975 b.el.setWidth(width);
30977 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30979 b.el.setHeight(height);
30983 var positions = [];
30986 x : maxX - this.unitWidth * 2 - this.gutter,
30991 x : maxX - this.unitWidth,
30992 y : minY + (this.unitWidth + this.gutter) * 2
30996 x : maxX - this.unitWidth * 3 - this.gutter * 2,
31000 Roo.each(eItems, function(b,k){
31002 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31008 getVerticalOneBoxColPositions : function(x, y, box)
31012 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31014 if(box[0].size == 'md-left'){
31018 if(box[0].size == 'md-right'){
31023 x : x + (this.unitWidth + this.gutter) * rand,
31030 getVerticalTwoBoxColPositions : function(x, y, box)
31034 if(box[0].size == 'xs'){
31038 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31042 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31056 x : x + (this.unitWidth + this.gutter) * 2,
31057 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31064 getVerticalThreeBoxColPositions : function(x, y, box)
31068 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31076 x : x + (this.unitWidth + this.gutter) * 1,
31081 x : x + (this.unitWidth + this.gutter) * 2,
31089 if(box[0].size == 'xs' && box[1].size == 'xs'){
31098 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31102 x : x + (this.unitWidth + this.gutter) * 1,
31116 x : x + (this.unitWidth + this.gutter) * 2,
31121 x : x + (this.unitWidth + this.gutter) * 2,
31122 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31129 getVerticalFourBoxColPositions : function(x, y, box)
31133 if(box[0].size == 'xs'){
31142 y : y + (this.unitHeight + this.gutter) * 1
31147 y : y + (this.unitHeight + this.gutter) * 2
31151 x : x + (this.unitWidth + this.gutter) * 1,
31165 x : x + (this.unitWidth + this.gutter) * 2,
31170 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31171 y : y + (this.unitHeight + this.gutter) * 1
31175 x : x + (this.unitWidth + this.gutter) * 2,
31176 y : y + (this.unitWidth + this.gutter) * 2
31183 getHorizontalOneBoxColPositions : function(maxX, minY, box)
31187 if(box[0].size == 'md-left'){
31189 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31196 if(box[0].size == 'md-right'){
31198 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31199 y : minY + (this.unitWidth + this.gutter) * 1
31205 var rand = Math.floor(Math.random() * (4 - box[0].y));
31208 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31209 y : minY + (this.unitWidth + this.gutter) * rand
31216 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31220 if(box[0].size == 'xs'){
31223 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31228 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31229 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31237 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31242 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31243 y : minY + (this.unitWidth + this.gutter) * 2
31250 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31254 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31257 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31262 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31263 y : minY + (this.unitWidth + this.gutter) * 1
31267 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31268 y : minY + (this.unitWidth + this.gutter) * 2
31275 if(box[0].size == 'xs' && box[1].size == 'xs'){
31278 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31283 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31288 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31289 y : minY + (this.unitWidth + this.gutter) * 1
31297 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31302 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31303 y : minY + (this.unitWidth + this.gutter) * 2
31307 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31308 y : minY + (this.unitWidth + this.gutter) * 2
31315 getHorizontalFourBoxColPositions : function(maxX, minY, box)
31319 if(box[0].size == 'xs'){
31322 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31327 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31332 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),
31337 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31338 y : minY + (this.unitWidth + this.gutter) * 1
31346 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31351 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31352 y : minY + (this.unitWidth + this.gutter) * 2
31356 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31357 y : minY + (this.unitWidth + this.gutter) * 2
31361 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),
31362 y : minY + (this.unitWidth + this.gutter) * 2
31370 * remove a Masonry Brick
31371 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31373 removeBrick : function(brick_id)
31379 for (var i = 0; i<this.bricks.length; i++) {
31380 if (this.bricks[i].id == brick_id) {
31381 this.bricks.splice(i,1);
31382 this.el.dom.removeChild(Roo.get(brick_id).dom);
31389 * adds a Masonry Brick
31390 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31392 addBrick : function(cfg)
31394 var cn = new Roo.bootstrap.MasonryBrick(cfg);
31395 //this.register(cn);
31396 cn.parentId = this.id;
31397 cn.onRender(this.el, null);
31402 * register a Masonry Brick
31403 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31406 register : function(brick)
31408 this.bricks.push(brick);
31409 brick.masonryId = this.id;
31413 * clear all the Masonry Brick
31415 clearAll : function()
31418 //this.getChildContainer().dom.innerHTML = "";
31419 this.el.dom.innerHTML = '';
31422 getSelected : function()
31424 if (!this.selectedBrick) {
31428 return this.selectedBrick;
31432 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31436 * register a Masonry Layout
31437 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31440 register : function(layout)
31442 this.groups[layout.id] = layout;
31445 * fetch a Masonry Layout based on the masonry layout ID
31446 * @param {string} the masonry layout to add
31447 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31450 get: function(layout_id) {
31451 if (typeof(this.groups[layout_id]) == 'undefined') {
31454 return this.groups[layout_id] ;
31466 * http://masonry.desandro.com
31468 * The idea is to render all the bricks based on vertical width...
31470 * The original code extends 'outlayer' - we might need to use that....
31476 * @class Roo.bootstrap.LayoutMasonryAuto
31477 * @extends Roo.bootstrap.Component
31478 * Bootstrap Layout Masonry class
31481 * Create a new Element
31482 * @param {Object} config The config object
31485 Roo.bootstrap.LayoutMasonryAuto = function(config){
31486 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31489 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
31492 * @cfg {Boolean} isFitWidth - resize the width..
31494 isFitWidth : false, // options..
31496 * @cfg {Boolean} isOriginLeft = left align?
31498 isOriginLeft : true,
31500 * @cfg {Boolean} isOriginTop = top align?
31502 isOriginTop : false,
31504 * @cfg {Boolean} isLayoutInstant = no animation?
31506 isLayoutInstant : false, // needed?
31508 * @cfg {Boolean} isResizingContainer = not sure if this is used..
31510 isResizingContainer : true,
31512 * @cfg {Number} columnWidth width of the columns
31518 * @cfg {Number} maxCols maximum number of columns
31523 * @cfg {Number} padHeight padding below box..
31529 * @cfg {Boolean} isAutoInitial defalut true
31532 isAutoInitial : true,
31538 initialColumnWidth : 0,
31539 currentSize : null,
31541 colYs : null, // array.
31548 bricks: null, //CompositeElement
31549 cols : 0, // array?
31550 // element : null, // wrapped now this.el
31551 _isLayoutInited : null,
31554 getAutoCreate : function(){
31558 cls: 'blog-masonary-wrapper ' + this.cls,
31560 cls : 'mas-boxes masonary'
31567 getChildContainer: function( )
31569 if (this.boxesEl) {
31570 return this.boxesEl;
31573 this.boxesEl = this.el.select('.mas-boxes').first();
31575 return this.boxesEl;
31579 initEvents : function()
31583 if(this.isAutoInitial){
31584 Roo.log('hook children rendered');
31585 this.on('childrenrendered', function() {
31586 Roo.log('children rendered');
31593 initial : function()
31595 this.reloadItems();
31597 this.currentSize = this.el.getBox(true);
31599 /// was window resize... - let's see if this works..
31600 Roo.EventManager.onWindowResize(this.resize, this);
31602 if(!this.isAutoInitial){
31607 this.layout.defer(500,this);
31610 reloadItems: function()
31612 this.bricks = this.el.select('.masonry-brick', true);
31614 this.bricks.each(function(b) {
31615 //Roo.log(b.getSize());
31616 if (!b.attr('originalwidth')) {
31617 b.attr('originalwidth', b.getSize().width);
31622 Roo.log(this.bricks.elements.length);
31625 resize : function()
31628 var cs = this.el.getBox(true);
31630 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
31631 Roo.log("no change in with or X");
31634 this.currentSize = cs;
31638 layout : function()
31641 this._resetLayout();
31642 //this._manageStamps();
31644 // don't animate first layout
31645 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31646 this.layoutItems( isInstant );
31648 // flag for initalized
31649 this._isLayoutInited = true;
31652 layoutItems : function( isInstant )
31654 //var items = this._getItemsForLayout( this.items );
31655 // original code supports filtering layout items.. we just ignore it..
31657 this._layoutItems( this.bricks , isInstant );
31659 this._postLayout();
31661 _layoutItems : function ( items , isInstant)
31663 //this.fireEvent( 'layout', this, items );
31666 if ( !items || !items.elements.length ) {
31667 // no items, emit event with empty array
31672 items.each(function(item) {
31673 Roo.log("layout item");
31675 // get x/y object from method
31676 var position = this._getItemLayoutPosition( item );
31678 position.item = item;
31679 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
31680 queue.push( position );
31683 this._processLayoutQueue( queue );
31685 /** Sets position of item in DOM
31686 * @param {Element} item
31687 * @param {Number} x - horizontal position
31688 * @param {Number} y - vertical position
31689 * @param {Boolean} isInstant - disables transitions
31691 _processLayoutQueue : function( queue )
31693 for ( var i=0, len = queue.length; i < len; i++ ) {
31694 var obj = queue[i];
31695 obj.item.position('absolute');
31696 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
31702 * Any logic you want to do after each layout,
31703 * i.e. size the container
31705 _postLayout : function()
31707 this.resizeContainer();
31710 resizeContainer : function()
31712 if ( !this.isResizingContainer ) {
31715 var size = this._getContainerSize();
31717 this.el.setSize(size.width,size.height);
31718 this.boxesEl.setSize(size.width,size.height);
31724 _resetLayout : function()
31726 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
31727 this.colWidth = this.el.getWidth();
31728 //this.gutter = this.el.getWidth();
31730 this.measureColumns();
31736 this.colYs.push( 0 );
31742 measureColumns : function()
31744 this.getContainerWidth();
31745 // if columnWidth is 0, default to outerWidth of first item
31746 if ( !this.columnWidth ) {
31747 var firstItem = this.bricks.first();
31748 Roo.log(firstItem);
31749 this.columnWidth = this.containerWidth;
31750 if (firstItem && firstItem.attr('originalwidth') ) {
31751 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
31753 // columnWidth fall back to item of first element
31754 Roo.log("set column width?");
31755 this.initialColumnWidth = this.columnWidth ;
31757 // if first elem has no width, default to size of container
31762 if (this.initialColumnWidth) {
31763 this.columnWidth = this.initialColumnWidth;
31768 // column width is fixed at the top - however if container width get's smaller we should
31771 // this bit calcs how man columns..
31773 var columnWidth = this.columnWidth += this.gutter;
31775 // calculate columns
31776 var containerWidth = this.containerWidth + this.gutter;
31778 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
31779 // fix rounding errors, typically with gutters
31780 var excess = columnWidth - containerWidth % columnWidth;
31783 // if overshoot is less than a pixel, round up, otherwise floor it
31784 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
31785 cols = Math[ mathMethod ]( cols );
31786 this.cols = Math.max( cols, 1 );
31787 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31789 // padding positioning..
31790 var totalColWidth = this.cols * this.columnWidth;
31791 var padavail = this.containerWidth - totalColWidth;
31792 // so for 2 columns - we need 3 'pads'
31794 var padNeeded = (1+this.cols) * this.padWidth;
31796 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
31798 this.columnWidth += padExtra
31799 //this.padWidth = Math.floor(padavail / ( this.cols));
31801 // adjust colum width so that padding is fixed??
31803 // we have 3 columns ... total = width * 3
31804 // we have X left over... that should be used by
31806 //if (this.expandC) {
31814 getContainerWidth : function()
31816 /* // container is parent if fit width
31817 var container = this.isFitWidth ? this.element.parentNode : this.element;
31818 // check that this.size and size are there
31819 // IE8 triggers resize on body size change, so they might not be
31821 var size = getSize( container ); //FIXME
31822 this.containerWidth = size && size.innerWidth; //FIXME
31825 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
31829 _getItemLayoutPosition : function( item ) // what is item?
31831 // we resize the item to our columnWidth..
31833 item.setWidth(this.columnWidth);
31834 item.autoBoxAdjust = false;
31836 var sz = item.getSize();
31838 // how many columns does this brick span
31839 var remainder = this.containerWidth % this.columnWidth;
31841 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
31842 // round if off by 1 pixel, otherwise use ceil
31843 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
31844 colSpan = Math.min( colSpan, this.cols );
31846 // normally this should be '1' as we dont' currently allow multi width columns..
31848 var colGroup = this._getColGroup( colSpan );
31849 // get the minimum Y value from the columns
31850 var minimumY = Math.min.apply( Math, colGroup );
31851 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
31853 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
31855 // position the brick
31857 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
31858 y: this.currentSize.y + minimumY + this.padHeight
31862 // apply setHeight to necessary columns
31863 var setHeight = minimumY + sz.height + this.padHeight;
31864 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
31866 var setSpan = this.cols + 1 - colGroup.length;
31867 for ( var i = 0; i < setSpan; i++ ) {
31868 this.colYs[ shortColIndex + i ] = setHeight ;
31875 * @param {Number} colSpan - number of columns the element spans
31876 * @returns {Array} colGroup
31878 _getColGroup : function( colSpan )
31880 if ( colSpan < 2 ) {
31881 // if brick spans only one column, use all the column Ys
31886 // how many different places could this brick fit horizontally
31887 var groupCount = this.cols + 1 - colSpan;
31888 // for each group potential horizontal position
31889 for ( var i = 0; i < groupCount; i++ ) {
31890 // make an array of colY values for that one group
31891 var groupColYs = this.colYs.slice( i, i + colSpan );
31892 // and get the max value of the array
31893 colGroup[i] = Math.max.apply( Math, groupColYs );
31898 _manageStamp : function( stamp )
31900 var stampSize = stamp.getSize();
31901 var offset = stamp.getBox();
31902 // get the columns that this stamp affects
31903 var firstX = this.isOriginLeft ? offset.x : offset.right;
31904 var lastX = firstX + stampSize.width;
31905 var firstCol = Math.floor( firstX / this.columnWidth );
31906 firstCol = Math.max( 0, firstCol );
31908 var lastCol = Math.floor( lastX / this.columnWidth );
31909 // lastCol should not go over if multiple of columnWidth #425
31910 lastCol -= lastX % this.columnWidth ? 0 : 1;
31911 lastCol = Math.min( this.cols - 1, lastCol );
31913 // set colYs to bottom of the stamp
31914 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
31917 for ( var i = firstCol; i <= lastCol; i++ ) {
31918 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
31923 _getContainerSize : function()
31925 this.maxY = Math.max.apply( Math, this.colYs );
31930 if ( this.isFitWidth ) {
31931 size.width = this._getContainerFitWidth();
31937 _getContainerFitWidth : function()
31939 var unusedCols = 0;
31940 // count unused columns
31943 if ( this.colYs[i] !== 0 ) {
31948 // fit container to columns that have been used
31949 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
31952 needsResizeLayout : function()
31954 var previousWidth = this.containerWidth;
31955 this.getContainerWidth();
31956 return previousWidth !== this.containerWidth;
31971 * @class Roo.bootstrap.MasonryBrick
31972 * @extends Roo.bootstrap.Component
31973 * Bootstrap MasonryBrick class
31976 * Create a new MasonryBrick
31977 * @param {Object} config The config object
31980 Roo.bootstrap.MasonryBrick = function(config){
31982 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
31984 Roo.bootstrap.MasonryBrick.register(this);
31990 * When a MasonryBrick is clcik
31991 * @param {Roo.bootstrap.MasonryBrick} this
31992 * @param {Roo.EventObject} e
31998 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
32001 * @cfg {String} title
32005 * @cfg {String} html
32009 * @cfg {String} bgimage
32013 * @cfg {String} videourl
32017 * @cfg {String} cls
32021 * @cfg {String} href
32025 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32030 * @cfg {String} placetitle (center|bottom)
32035 * @cfg {Boolean} isFitContainer defalut true
32037 isFitContainer : true,
32040 * @cfg {Boolean} preventDefault defalut false
32042 preventDefault : false,
32045 * @cfg {Boolean} inverse defalut false
32047 maskInverse : false,
32049 getAutoCreate : function()
32051 if(!this.isFitContainer){
32052 return this.getSplitAutoCreate();
32055 var cls = 'masonry-brick masonry-brick-full';
32057 if(this.href.length){
32058 cls += ' masonry-brick-link';
32061 if(this.bgimage.length){
32062 cls += ' masonry-brick-image';
32065 if(this.maskInverse){
32066 cls += ' mask-inverse';
32069 if(!this.html.length && !this.maskInverse && !this.videourl.length){
32070 cls += ' enable-mask';
32074 cls += ' masonry-' + this.size + '-brick';
32077 if(this.placetitle.length){
32079 switch (this.placetitle) {
32081 cls += ' masonry-center-title';
32084 cls += ' masonry-bottom-title';
32091 if(!this.html.length && !this.bgimage.length){
32092 cls += ' masonry-center-title';
32095 if(!this.html.length && this.bgimage.length){
32096 cls += ' masonry-bottom-title';
32101 cls += ' ' + this.cls;
32105 tag: (this.href.length) ? 'a' : 'div',
32110 cls: 'masonry-brick-mask'
32114 cls: 'masonry-brick-paragraph',
32120 if(this.href.length){
32121 cfg.href = this.href;
32124 var cn = cfg.cn[1].cn;
32126 if(this.title.length){
32129 cls: 'masonry-brick-title',
32134 if(this.html.length){
32137 cls: 'masonry-brick-text',
32142 if (!this.title.length && !this.html.length) {
32143 cfg.cn[1].cls += ' hide';
32146 if(this.bgimage.length){
32149 cls: 'masonry-brick-image-view',
32154 if(this.videourl.length){
32155 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32156 // youtube support only?
32159 cls: 'masonry-brick-image-view',
32162 allowfullscreen : true
32170 getSplitAutoCreate : function()
32172 var cls = 'masonry-brick masonry-brick-split';
32174 if(this.href.length){
32175 cls += ' masonry-brick-link';
32178 if(this.bgimage.length){
32179 cls += ' masonry-brick-image';
32183 cls += ' masonry-' + this.size + '-brick';
32186 switch (this.placetitle) {
32188 cls += ' masonry-center-title';
32191 cls += ' masonry-bottom-title';
32194 if(!this.bgimage.length){
32195 cls += ' masonry-center-title';
32198 if(this.bgimage.length){
32199 cls += ' masonry-bottom-title';
32205 cls += ' ' + this.cls;
32209 tag: (this.href.length) ? 'a' : 'div',
32214 cls: 'masonry-brick-split-head',
32218 cls: 'masonry-brick-paragraph',
32225 cls: 'masonry-brick-split-body',
32231 if(this.href.length){
32232 cfg.href = this.href;
32235 if(this.title.length){
32236 cfg.cn[0].cn[0].cn.push({
32238 cls: 'masonry-brick-title',
32243 if(this.html.length){
32244 cfg.cn[1].cn.push({
32246 cls: 'masonry-brick-text',
32251 if(this.bgimage.length){
32252 cfg.cn[0].cn.push({
32254 cls: 'masonry-brick-image-view',
32259 if(this.videourl.length){
32260 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32261 // youtube support only?
32262 cfg.cn[0].cn.cn.push({
32264 cls: 'masonry-brick-image-view',
32267 allowfullscreen : true
32274 initEvents: function()
32276 switch (this.size) {
32309 this.el.on('touchstart', this.onTouchStart, this);
32310 this.el.on('touchmove', this.onTouchMove, this);
32311 this.el.on('touchend', this.onTouchEnd, this);
32312 this.el.on('contextmenu', this.onContextMenu, this);
32314 this.el.on('mouseenter' ,this.enter, this);
32315 this.el.on('mouseleave', this.leave, this);
32316 this.el.on('click', this.onClick, this);
32319 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32320 this.parent().bricks.push(this);
32325 onClick: function(e, el)
32327 var time = this.endTimer - this.startTimer;
32328 // Roo.log(e.preventDefault());
32331 e.preventDefault();
32336 if(!this.preventDefault){
32340 e.preventDefault();
32342 if (this.activcClass != '') {
32343 this.selectBrick();
32346 this.fireEvent('click', this);
32349 enter: function(e, el)
32351 e.preventDefault();
32353 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32357 if(this.bgimage.length && this.html.length){
32358 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32362 leave: function(e, el)
32364 e.preventDefault();
32366 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32370 if(this.bgimage.length && this.html.length){
32371 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32375 onTouchStart: function(e, el)
32377 // e.preventDefault();
32379 this.touchmoved = false;
32381 if(!this.isFitContainer){
32385 if(!this.bgimage.length || !this.html.length){
32389 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32391 this.timer = new Date().getTime();
32395 onTouchMove: function(e, el)
32397 this.touchmoved = true;
32400 onContextMenu : function(e,el)
32402 e.preventDefault();
32403 e.stopPropagation();
32407 onTouchEnd: function(e, el)
32409 // e.preventDefault();
32411 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32418 if(!this.bgimage.length || !this.html.length){
32420 if(this.href.length){
32421 window.location.href = this.href;
32427 if(!this.isFitContainer){
32431 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32433 window.location.href = this.href;
32436 //selection on single brick only
32437 selectBrick : function() {
32439 if (!this.parentId) {
32443 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32444 var index = m.selectedBrick.indexOf(this.id);
32447 m.selectedBrick.splice(index,1);
32448 this.el.removeClass(this.activeClass);
32452 for(var i = 0; i < m.selectedBrick.length; i++) {
32453 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32454 b.el.removeClass(b.activeClass);
32457 m.selectedBrick = [];
32459 m.selectedBrick.push(this.id);
32460 this.el.addClass(this.activeClass);
32466 Roo.apply(Roo.bootstrap.MasonryBrick, {
32469 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32471 * register a Masonry Brick
32472 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32475 register : function(brick)
32477 //this.groups[brick.id] = brick;
32478 this.groups.add(brick.id, brick);
32481 * fetch a masonry brick based on the masonry brick ID
32482 * @param {string} the masonry brick to add
32483 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
32486 get: function(brick_id)
32488 // if (typeof(this.groups[brick_id]) == 'undefined') {
32491 // return this.groups[brick_id] ;
32493 if(this.groups.key(brick_id)) {
32494 return this.groups.key(brick_id);
32512 * @class Roo.bootstrap.Brick
32513 * @extends Roo.bootstrap.Component
32514 * Bootstrap Brick class
32517 * Create a new Brick
32518 * @param {Object} config The config object
32521 Roo.bootstrap.Brick = function(config){
32522 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
32528 * When a Brick is click
32529 * @param {Roo.bootstrap.Brick} this
32530 * @param {Roo.EventObject} e
32536 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
32539 * @cfg {String} title
32543 * @cfg {String} html
32547 * @cfg {String} bgimage
32551 * @cfg {String} cls
32555 * @cfg {String} href
32559 * @cfg {String} video
32563 * @cfg {Boolean} square
32567 getAutoCreate : function()
32569 var cls = 'roo-brick';
32571 if(this.href.length){
32572 cls += ' roo-brick-link';
32575 if(this.bgimage.length){
32576 cls += ' roo-brick-image';
32579 if(!this.html.length && !this.bgimage.length){
32580 cls += ' roo-brick-center-title';
32583 if(!this.html.length && this.bgimage.length){
32584 cls += ' roo-brick-bottom-title';
32588 cls += ' ' + this.cls;
32592 tag: (this.href.length) ? 'a' : 'div',
32597 cls: 'roo-brick-paragraph',
32603 if(this.href.length){
32604 cfg.href = this.href;
32607 var cn = cfg.cn[0].cn;
32609 if(this.title.length){
32612 cls: 'roo-brick-title',
32617 if(this.html.length){
32620 cls: 'roo-brick-text',
32627 if(this.bgimage.length){
32630 cls: 'roo-brick-image-view',
32638 initEvents: function()
32640 if(this.title.length || this.html.length){
32641 this.el.on('mouseenter' ,this.enter, this);
32642 this.el.on('mouseleave', this.leave, this);
32645 Roo.EventManager.onWindowResize(this.resize, this);
32647 if(this.bgimage.length){
32648 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
32649 this.imageEl.on('load', this.onImageLoad, this);
32656 onImageLoad : function()
32661 resize : function()
32663 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
32665 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
32667 if(this.bgimage.length){
32668 var image = this.el.select('.roo-brick-image-view', true).first();
32670 image.setWidth(paragraph.getWidth());
32673 image.setHeight(paragraph.getWidth());
32676 this.el.setHeight(image.getHeight());
32677 paragraph.setHeight(image.getHeight());
32683 enter: function(e, el)
32685 e.preventDefault();
32687 if(this.bgimage.length){
32688 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
32689 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
32693 leave: function(e, el)
32695 e.preventDefault();
32697 if(this.bgimage.length){
32698 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
32699 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
32715 * @class Roo.bootstrap.NumberField
32716 * @extends Roo.bootstrap.Input
32717 * Bootstrap NumberField class
32723 * Create a new NumberField
32724 * @param {Object} config The config object
32727 Roo.bootstrap.NumberField = function(config){
32728 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
32731 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
32734 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
32736 allowDecimals : true,
32738 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
32740 decimalSeparator : ".",
32742 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
32744 decimalPrecision : 2,
32746 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
32748 allowNegative : true,
32750 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
32752 minValue : Number.NEGATIVE_INFINITY,
32754 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
32756 maxValue : Number.MAX_VALUE,
32758 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
32760 minText : "The minimum value for this field is {0}",
32762 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
32764 maxText : "The maximum value for this field is {0}",
32766 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
32767 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
32769 nanText : "{0} is not a valid number",
32771 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
32776 initEvents : function()
32778 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
32780 var allowed = "0123456789";
32782 if(this.allowDecimals){
32783 allowed += this.decimalSeparator;
32786 if(this.allowNegative){
32790 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
32792 var keyPress = function(e){
32794 var k = e.getKey();
32796 var c = e.getCharCode();
32799 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
32800 allowed.indexOf(String.fromCharCode(c)) === -1
32806 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
32810 if(allowed.indexOf(String.fromCharCode(c)) === -1){
32815 this.el.on("keypress", keyPress, this);
32818 validateValue : function(value)
32821 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
32825 var num = this.parseValue(value);
32828 this.markInvalid(String.format(this.nanText, value));
32832 if(num < this.minValue){
32833 this.markInvalid(String.format(this.minText, this.minValue));
32837 if(num > this.maxValue){
32838 this.markInvalid(String.format(this.maxText, this.maxValue));
32845 getValue : function()
32847 return this.fixPrecision(this.parseValue(Roo.bootstrap.NumberField.superclass.getValue.call(this)));
32850 parseValue : function(value)
32852 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
32853 return isNaN(value) ? '' : value;
32856 fixPrecision : function(value)
32858 var nan = isNaN(value);
32860 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
32861 return nan ? '' : value;
32863 return parseFloat(value).toFixed(this.decimalPrecision);
32866 setValue : function(v)
32868 v = this.fixPrecision(v);
32869 Roo.bootstrap.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
32872 decimalPrecisionFcn : function(v)
32874 return Math.floor(v);
32877 beforeBlur : function()
32883 var v = this.parseValue(this.getRawValue());
32898 * @class Roo.bootstrap.DocumentSlider
32899 * @extends Roo.bootstrap.Component
32900 * Bootstrap DocumentSlider class
32903 * Create a new DocumentViewer
32904 * @param {Object} config The config object
32907 Roo.bootstrap.DocumentSlider = function(config){
32908 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
32915 * Fire after initEvent
32916 * @param {Roo.bootstrap.DocumentSlider} this
32921 * Fire after update
32922 * @param {Roo.bootstrap.DocumentSlider} this
32928 * @param {Roo.bootstrap.DocumentSlider} this
32934 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
32940 getAutoCreate : function()
32944 cls : 'roo-document-slider',
32948 cls : 'roo-document-slider-header',
32952 cls : 'roo-document-slider-header-title'
32958 cls : 'roo-document-slider-body',
32962 cls : 'roo-document-slider-prev',
32966 cls : 'fa fa-chevron-left'
32972 cls : 'roo-document-slider-thumb',
32976 cls : 'roo-document-slider-image'
32982 cls : 'roo-document-slider-next',
32986 cls : 'fa fa-chevron-right'
32998 initEvents : function()
33000 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33001 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33003 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33004 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33006 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33007 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33009 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33010 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33012 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33013 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33015 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33016 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33018 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33019 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33021 this.thumbEl.on('click', this.onClick, this);
33023 this.prevIndicator.on('click', this.prev, this);
33025 this.nextIndicator.on('click', this.next, this);
33029 initial : function()
33031 if(this.files.length){
33032 this.indicator = 1;
33036 this.fireEvent('initial', this);
33039 update : function()
33041 this.imageEl.attr('src', this.files[this.indicator - 1]);
33043 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33045 this.prevIndicator.show();
33047 if(this.indicator == 1){
33048 this.prevIndicator.hide();
33051 this.nextIndicator.show();
33053 if(this.indicator == this.files.length){
33054 this.nextIndicator.hide();
33057 this.thumbEl.scrollTo('top');
33059 this.fireEvent('update', this);
33062 onClick : function(e)
33064 e.preventDefault();
33066 this.fireEvent('click', this);
33071 e.preventDefault();
33073 this.indicator = Math.max(1, this.indicator - 1);
33080 e.preventDefault();
33082 this.indicator = Math.min(this.files.length, this.indicator + 1);
33096 * @class Roo.bootstrap.RadioSet
33097 * @extends Roo.bootstrap.Input
33098 * Bootstrap RadioSet class
33099 * @cfg {String} indicatorpos (left|right) default left
33100 * @cfg {Boolean} inline (true|false) inline the element (default true)
33101 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33103 * Create a new RadioSet
33104 * @param {Object} config The config object
33107 Roo.bootstrap.RadioSet = function(config){
33109 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33113 Roo.bootstrap.RadioSet.register(this);
33118 * Fires when the element is checked or unchecked.
33119 * @param {Roo.bootstrap.RadioSet} this This radio
33120 * @param {Roo.bootstrap.Radio} item The checked item
33127 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
33135 indicatorpos : 'left',
33137 getAutoCreate : function()
33141 cls : 'roo-radio-set-label',
33145 html : this.fieldLabel
33150 if(this.indicatorpos == 'left'){
33153 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33154 tooltip : 'This field is required'
33159 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33160 tooltip : 'This field is required'
33166 cls : 'roo-radio-set-items'
33169 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33171 if (align === 'left' && this.fieldLabel.length) {
33174 cls : "roo-radio-set-right",
33180 if(this.labelWidth > 12){
33181 label.style = "width: " + this.labelWidth + 'px';
33184 if(this.labelWidth < 13 && this.labelmd == 0){
33185 this.labelmd = this.labelWidth;
33188 if(this.labellg > 0){
33189 label.cls += ' col-lg-' + this.labellg;
33190 items.cls += ' col-lg-' + (12 - this.labellg);
33193 if(this.labelmd > 0){
33194 label.cls += ' col-md-' + this.labelmd;
33195 items.cls += ' col-md-' + (12 - this.labelmd);
33198 if(this.labelsm > 0){
33199 label.cls += ' col-sm-' + this.labelsm;
33200 items.cls += ' col-sm-' + (12 - this.labelsm);
33203 if(this.labelxs > 0){
33204 label.cls += ' col-xs-' + this.labelxs;
33205 items.cls += ' col-xs-' + (12 - this.labelxs);
33211 cls : 'roo-radio-set',
33215 cls : 'roo-radio-set-input',
33218 value : this.value ? this.value : ''
33225 if(this.weight.length){
33226 cfg.cls += ' roo-radio-' + this.weight;
33230 cfg.cls += ' roo-radio-set-inline';
33237 initEvents : function()
33239 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33240 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33242 if(!this.fieldLabel.length){
33243 this.labelEl.hide();
33246 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33247 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33249 this.indicatorEl().setVisibilityMode(Roo.Element.DISPLAY);
33250 this.indicatorEl().hide();
33252 this.originalValue = this.getValue();
33256 inputEl: function ()
33258 return this.el.select('.roo-radio-set-input', true).first();
33261 getChildContainer : function()
33263 return this.itemsEl;
33266 register : function(item)
33268 this.radioes.push(item);
33272 validate : function()
33276 Roo.each(this.radioes, function(i){
33285 if(this.allowBlank) {
33289 if(this.disabled || valid){
33294 this.markInvalid();
33299 markValid : function()
33301 if(this.labelEl.isVisible(true)){
33302 this.indicatorEl().hide();
33305 this.el.removeClass([this.invalidClass, this.validClass]);
33306 this.el.addClass(this.validClass);
33308 this.fireEvent('valid', this);
33311 markInvalid : function(msg)
33313 if(this.allowBlank || this.disabled){
33317 if(this.labelEl.isVisible(true)){
33318 this.indicatorEl().show();
33321 this.el.removeClass([this.invalidClass, this.validClass]);
33322 this.el.addClass(this.invalidClass);
33324 this.fireEvent('invalid', this, msg);
33328 setValue : function(v, suppressEvent)
33332 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33335 Roo.each(this.radioes, function(i){
33338 i.el.removeClass('checked');
33340 if(i.value === v || i.value.toString() === v.toString()){
33342 i.el.addClass('checked');
33344 if(suppressEvent !== true){
33345 this.fireEvent('check', this, i);
33354 clearInvalid : function(){
33356 if(!this.el || this.preventMark){
33360 this.el.removeClass([this.invalidClass]);
33362 this.fireEvent('valid', this);
33367 Roo.apply(Roo.bootstrap.RadioSet, {
33371 register : function(set)
33373 this.groups[set.name] = set;
33376 get: function(name)
33378 if (typeof(this.groups[name]) == 'undefined') {
33382 return this.groups[name] ;
33388 * Ext JS Library 1.1.1
33389 * Copyright(c) 2006-2007, Ext JS, LLC.
33391 * Originally Released Under LGPL - original licence link has changed is not relivant.
33394 * <script type="text/javascript">
33399 * @class Roo.bootstrap.SplitBar
33400 * @extends Roo.util.Observable
33401 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
33405 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
33406 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
33407 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
33408 split.minSize = 100;
33409 split.maxSize = 600;
33410 split.animate = true;
33411 split.on('moved', splitterMoved);
33414 * Create a new SplitBar
33415 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
33416 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
33417 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33418 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
33419 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
33420 position of the SplitBar).
33422 Roo.bootstrap.SplitBar = function(cfg){
33427 // dragElement : elm
33428 // resizingElement: el,
33430 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
33431 // placement : Roo.bootstrap.SplitBar.LEFT ,
33432 // existingProxy ???
33435 this.el = Roo.get(cfg.dragElement, true);
33436 this.el.dom.unselectable = "on";
33438 this.resizingEl = Roo.get(cfg.resizingElement, true);
33442 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33443 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
33446 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
33449 * The minimum size of the resizing element. (Defaults to 0)
33455 * The maximum size of the resizing element. (Defaults to 2000)
33458 this.maxSize = 2000;
33461 * Whether to animate the transition to the new size
33464 this.animate = false;
33467 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
33470 this.useShim = false;
33475 if(!cfg.existingProxy){
33477 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
33479 this.proxy = Roo.get(cfg.existingProxy).dom;
33482 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
33485 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
33488 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
33491 this.dragSpecs = {};
33494 * @private The adapter to use to positon and resize elements
33496 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33497 this.adapter.init(this);
33499 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33501 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
33502 this.el.addClass("roo-splitbar-h");
33505 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
33506 this.el.addClass("roo-splitbar-v");
33512 * Fires when the splitter is moved (alias for {@link #event-moved})
33513 * @param {Roo.bootstrap.SplitBar} this
33514 * @param {Number} newSize the new width or height
33519 * Fires when the splitter is moved
33520 * @param {Roo.bootstrap.SplitBar} this
33521 * @param {Number} newSize the new width or height
33525 * @event beforeresize
33526 * Fires before the splitter is dragged
33527 * @param {Roo.bootstrap.SplitBar} this
33529 "beforeresize" : true,
33531 "beforeapply" : true
33534 Roo.util.Observable.call(this);
33537 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
33538 onStartProxyDrag : function(x, y){
33539 this.fireEvent("beforeresize", this);
33541 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
33543 o.enableDisplayMode("block");
33544 // all splitbars share the same overlay
33545 Roo.bootstrap.SplitBar.prototype.overlay = o;
33547 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33548 this.overlay.show();
33549 Roo.get(this.proxy).setDisplayed("block");
33550 var size = this.adapter.getElementSize(this);
33551 this.activeMinSize = this.getMinimumSize();;
33552 this.activeMaxSize = this.getMaximumSize();;
33553 var c1 = size - this.activeMinSize;
33554 var c2 = Math.max(this.activeMaxSize - size, 0);
33555 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33556 this.dd.resetConstraints();
33557 this.dd.setXConstraint(
33558 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
33559 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
33561 this.dd.setYConstraint(0, 0);
33563 this.dd.resetConstraints();
33564 this.dd.setXConstraint(0, 0);
33565 this.dd.setYConstraint(
33566 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
33567 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
33570 this.dragSpecs.startSize = size;
33571 this.dragSpecs.startPoint = [x, y];
33572 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
33576 * @private Called after the drag operation by the DDProxy
33578 onEndProxyDrag : function(e){
33579 Roo.get(this.proxy).setDisplayed(false);
33580 var endPoint = Roo.lib.Event.getXY(e);
33582 this.overlay.hide();
33585 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33586 newSize = this.dragSpecs.startSize +
33587 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
33588 endPoint[0] - this.dragSpecs.startPoint[0] :
33589 this.dragSpecs.startPoint[0] - endPoint[0]
33592 newSize = this.dragSpecs.startSize +
33593 (this.placement == Roo.bootstrap.SplitBar.TOP ?
33594 endPoint[1] - this.dragSpecs.startPoint[1] :
33595 this.dragSpecs.startPoint[1] - endPoint[1]
33598 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
33599 if(newSize != this.dragSpecs.startSize){
33600 if(this.fireEvent('beforeapply', this, newSize) !== false){
33601 this.adapter.setElementSize(this, newSize);
33602 this.fireEvent("moved", this, newSize);
33603 this.fireEvent("resize", this, newSize);
33609 * Get the adapter this SplitBar uses
33610 * @return The adapter object
33612 getAdapter : function(){
33613 return this.adapter;
33617 * Set the adapter this SplitBar uses
33618 * @param {Object} adapter A SplitBar adapter object
33620 setAdapter : function(adapter){
33621 this.adapter = adapter;
33622 this.adapter.init(this);
33626 * Gets the minimum size for the resizing element
33627 * @return {Number} The minimum size
33629 getMinimumSize : function(){
33630 return this.minSize;
33634 * Sets the minimum size for the resizing element
33635 * @param {Number} minSize The minimum size
33637 setMinimumSize : function(minSize){
33638 this.minSize = minSize;
33642 * Gets the maximum size for the resizing element
33643 * @return {Number} The maximum size
33645 getMaximumSize : function(){
33646 return this.maxSize;
33650 * Sets the maximum size for the resizing element
33651 * @param {Number} maxSize The maximum size
33653 setMaximumSize : function(maxSize){
33654 this.maxSize = maxSize;
33658 * Sets the initialize size for the resizing element
33659 * @param {Number} size The initial size
33661 setCurrentSize : function(size){
33662 var oldAnimate = this.animate;
33663 this.animate = false;
33664 this.adapter.setElementSize(this, size);
33665 this.animate = oldAnimate;
33669 * Destroy this splitbar.
33670 * @param {Boolean} removeEl True to remove the element
33672 destroy : function(removeEl){
33674 this.shim.remove();
33677 this.proxy.parentNode.removeChild(this.proxy);
33685 * @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.
33687 Roo.bootstrap.SplitBar.createProxy = function(dir){
33688 var proxy = new Roo.Element(document.createElement("div"));
33689 proxy.unselectable();
33690 var cls = 'roo-splitbar-proxy';
33691 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
33692 document.body.appendChild(proxy.dom);
33697 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
33698 * Default Adapter. It assumes the splitter and resizing element are not positioned
33699 * elements and only gets/sets the width of the element. Generally used for table based layouts.
33701 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
33704 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
33705 // do nothing for now
33706 init : function(s){
33710 * Called before drag operations to get the current size of the resizing element.
33711 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33713 getElementSize : function(s){
33714 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33715 return s.resizingEl.getWidth();
33717 return s.resizingEl.getHeight();
33722 * Called after drag operations to set the size of the resizing element.
33723 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33724 * @param {Number} newSize The new size to set
33725 * @param {Function} onComplete A function to be invoked when resizing is complete
33727 setElementSize : function(s, newSize, onComplete){
33728 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33730 s.resizingEl.setWidth(newSize);
33732 onComplete(s, newSize);
33735 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
33740 s.resizingEl.setHeight(newSize);
33742 onComplete(s, newSize);
33745 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
33752 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
33753 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
33754 * Adapter that moves the splitter element to align with the resized sizing element.
33755 * Used with an absolute positioned SplitBar.
33756 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
33757 * document.body, make sure you assign an id to the body element.
33759 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
33760 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33761 this.container = Roo.get(container);
33764 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
33765 init : function(s){
33766 this.basic.init(s);
33769 getElementSize : function(s){
33770 return this.basic.getElementSize(s);
33773 setElementSize : function(s, newSize, onComplete){
33774 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
33777 moveSplitter : function(s){
33778 var yes = Roo.bootstrap.SplitBar;
33779 switch(s.placement){
33781 s.el.setX(s.resizingEl.getRight());
33784 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
33787 s.el.setY(s.resizingEl.getBottom());
33790 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
33797 * Orientation constant - Create a vertical SplitBar
33801 Roo.bootstrap.SplitBar.VERTICAL = 1;
33804 * Orientation constant - Create a horizontal SplitBar
33808 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
33811 * Placement constant - The resizing element is to the left of the splitter element
33815 Roo.bootstrap.SplitBar.LEFT = 1;
33818 * Placement constant - The resizing element is to the right of the splitter element
33822 Roo.bootstrap.SplitBar.RIGHT = 2;
33825 * Placement constant - The resizing element is positioned above the splitter element
33829 Roo.bootstrap.SplitBar.TOP = 3;
33832 * Placement constant - The resizing element is positioned under splitter element
33836 Roo.bootstrap.SplitBar.BOTTOM = 4;
33837 Roo.namespace("Roo.bootstrap.layout");/*
33839 * Ext JS Library 1.1.1
33840 * Copyright(c) 2006-2007, Ext JS, LLC.
33842 * Originally Released Under LGPL - original licence link has changed is not relivant.
33845 * <script type="text/javascript">
33849 * @class Roo.bootstrap.layout.Manager
33850 * @extends Roo.bootstrap.Component
33851 * Base class for layout managers.
33853 Roo.bootstrap.layout.Manager = function(config)
33855 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
33861 /** false to disable window resize monitoring @type Boolean */
33862 this.monitorWindowResize = true;
33867 * Fires when a layout is performed.
33868 * @param {Roo.LayoutManager} this
33872 * @event regionresized
33873 * Fires when the user resizes a region.
33874 * @param {Roo.LayoutRegion} region The resized region
33875 * @param {Number} newSize The new size (width for east/west, height for north/south)
33877 "regionresized" : true,
33879 * @event regioncollapsed
33880 * Fires when a region is collapsed.
33881 * @param {Roo.LayoutRegion} region The collapsed region
33883 "regioncollapsed" : true,
33885 * @event regionexpanded
33886 * Fires when a region is expanded.
33887 * @param {Roo.LayoutRegion} region The expanded region
33889 "regionexpanded" : true
33891 this.updating = false;
33894 this.el = Roo.get(config.el);
33900 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
33905 monitorWindowResize : true,
33911 onRender : function(ct, position)
33914 this.el = Roo.get(ct);
33917 //this.fireEvent('render',this);
33921 initEvents: function()
33925 // ie scrollbar fix
33926 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
33927 document.body.scroll = "no";
33928 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
33929 this.el.position('relative');
33931 this.id = this.el.id;
33932 this.el.addClass("roo-layout-container");
33933 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
33934 if(this.el.dom != document.body ) {
33935 this.el.on('resize', this.layout,this);
33936 this.el.on('show', this.layout,this);
33942 * Returns true if this layout is currently being updated
33943 * @return {Boolean}
33945 isUpdating : function(){
33946 return this.updating;
33950 * Suspend the LayoutManager from doing auto-layouts while
33951 * making multiple add or remove calls
33953 beginUpdate : function(){
33954 this.updating = true;
33958 * Restore auto-layouts and optionally disable the manager from performing a layout
33959 * @param {Boolean} noLayout true to disable a layout update
33961 endUpdate : function(noLayout){
33962 this.updating = false;
33968 layout: function(){
33972 onRegionResized : function(region, newSize){
33973 this.fireEvent("regionresized", region, newSize);
33977 onRegionCollapsed : function(region){
33978 this.fireEvent("regioncollapsed", region);
33981 onRegionExpanded : function(region){
33982 this.fireEvent("regionexpanded", region);
33986 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
33987 * performs box-model adjustments.
33988 * @return {Object} The size as an object {width: (the width), height: (the height)}
33990 getViewSize : function()
33993 if(this.el.dom != document.body){
33994 size = this.el.getSize();
33996 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
33998 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
33999 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34004 * Returns the Element this layout is bound to.
34005 * @return {Roo.Element}
34007 getEl : function(){
34012 * Returns the specified region.
34013 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34014 * @return {Roo.LayoutRegion}
34016 getRegion : function(target){
34017 return this.regions[target.toLowerCase()];
34020 onWindowResize : function(){
34021 if(this.monitorWindowResize){
34028 * Ext JS Library 1.1.1
34029 * Copyright(c) 2006-2007, Ext JS, LLC.
34031 * Originally Released Under LGPL - original licence link has changed is not relivant.
34034 * <script type="text/javascript">
34037 * @class Roo.bootstrap.layout.Border
34038 * @extends Roo.bootstrap.layout.Manager
34039 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34040 * please see: examples/bootstrap/nested.html<br><br>
34042 <b>The container the layout is rendered into can be either the body element or any other element.
34043 If it is not the body element, the container needs to either be an absolute positioned element,
34044 or you will need to add "position:relative" to the css of the container. You will also need to specify
34045 the container size if it is not the body element.</b>
34048 * Create a new Border
34049 * @param {Object} config Configuration options
34051 Roo.bootstrap.layout.Border = function(config){
34052 config = config || {};
34053 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34057 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34058 if(config[region]){
34059 config[region].region = region;
34060 this.addRegion(config[region]);
34066 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
34068 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34070 * Creates and adds a new region if it doesn't already exist.
34071 * @param {String} target The target region key (north, south, east, west or center).
34072 * @param {Object} config The regions config object
34073 * @return {BorderLayoutRegion} The new region
34075 addRegion : function(config)
34077 if(!this.regions[config.region]){
34078 var r = this.factory(config);
34079 this.bindRegion(r);
34081 return this.regions[config.region];
34085 bindRegion : function(r){
34086 this.regions[r.config.region] = r;
34088 r.on("visibilitychange", this.layout, this);
34089 r.on("paneladded", this.layout, this);
34090 r.on("panelremoved", this.layout, this);
34091 r.on("invalidated", this.layout, this);
34092 r.on("resized", this.onRegionResized, this);
34093 r.on("collapsed", this.onRegionCollapsed, this);
34094 r.on("expanded", this.onRegionExpanded, this);
34098 * Performs a layout update.
34100 layout : function()
34102 if(this.updating) {
34106 // render all the rebions if they have not been done alreayd?
34107 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34108 if(this.regions[region] && !this.regions[region].bodyEl){
34109 this.regions[region].onRender(this.el)
34113 var size = this.getViewSize();
34114 var w = size.width;
34115 var h = size.height;
34120 //var x = 0, y = 0;
34122 var rs = this.regions;
34123 var north = rs["north"];
34124 var south = rs["south"];
34125 var west = rs["west"];
34126 var east = rs["east"];
34127 var center = rs["center"];
34128 //if(this.hideOnLayout){ // not supported anymore
34129 //c.el.setStyle("display", "none");
34131 if(north && north.isVisible()){
34132 var b = north.getBox();
34133 var m = north.getMargins();
34134 b.width = w - (m.left+m.right);
34137 centerY = b.height + b.y + m.bottom;
34138 centerH -= centerY;
34139 north.updateBox(this.safeBox(b));
34141 if(south && south.isVisible()){
34142 var b = south.getBox();
34143 var m = south.getMargins();
34144 b.width = w - (m.left+m.right);
34146 var totalHeight = (b.height + m.top + m.bottom);
34147 b.y = h - totalHeight + m.top;
34148 centerH -= totalHeight;
34149 south.updateBox(this.safeBox(b));
34151 if(west && west.isVisible()){
34152 var b = west.getBox();
34153 var m = west.getMargins();
34154 b.height = centerH - (m.top+m.bottom);
34156 b.y = centerY + m.top;
34157 var totalWidth = (b.width + m.left + m.right);
34158 centerX += totalWidth;
34159 centerW -= totalWidth;
34160 west.updateBox(this.safeBox(b));
34162 if(east && east.isVisible()){
34163 var b = east.getBox();
34164 var m = east.getMargins();
34165 b.height = centerH - (m.top+m.bottom);
34166 var totalWidth = (b.width + m.left + m.right);
34167 b.x = w - totalWidth + m.left;
34168 b.y = centerY + m.top;
34169 centerW -= totalWidth;
34170 east.updateBox(this.safeBox(b));
34173 var m = center.getMargins();
34175 x: centerX + m.left,
34176 y: centerY + m.top,
34177 width: centerW - (m.left+m.right),
34178 height: centerH - (m.top+m.bottom)
34180 //if(this.hideOnLayout){
34181 //center.el.setStyle("display", "block");
34183 center.updateBox(this.safeBox(centerBox));
34186 this.fireEvent("layout", this);
34190 safeBox : function(box){
34191 box.width = Math.max(0, box.width);
34192 box.height = Math.max(0, box.height);
34197 * Adds a ContentPanel (or subclass) to this layout.
34198 * @param {String} target The target region key (north, south, east, west or center).
34199 * @param {Roo.ContentPanel} panel The panel to add
34200 * @return {Roo.ContentPanel} The added panel
34202 add : function(target, panel){
34204 target = target.toLowerCase();
34205 return this.regions[target].add(panel);
34209 * Remove a ContentPanel (or subclass) to this layout.
34210 * @param {String} target The target region key (north, south, east, west or center).
34211 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34212 * @return {Roo.ContentPanel} The removed panel
34214 remove : function(target, panel){
34215 target = target.toLowerCase();
34216 return this.regions[target].remove(panel);
34220 * Searches all regions for a panel with the specified id
34221 * @param {String} panelId
34222 * @return {Roo.ContentPanel} The panel or null if it wasn't found
34224 findPanel : function(panelId){
34225 var rs = this.regions;
34226 for(var target in rs){
34227 if(typeof rs[target] != "function"){
34228 var p = rs[target].getPanel(panelId);
34238 * Searches all regions for a panel with the specified id and activates (shows) it.
34239 * @param {String/ContentPanel} panelId The panels id or the panel itself
34240 * @return {Roo.ContentPanel} The shown panel or null
34242 showPanel : function(panelId) {
34243 var rs = this.regions;
34244 for(var target in rs){
34245 var r = rs[target];
34246 if(typeof r != "function"){
34247 if(r.hasPanel(panelId)){
34248 return r.showPanel(panelId);
34256 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34257 * @param {Roo.state.Provider} provider (optional) An alternate state provider
34260 restoreState : function(provider){
34262 provider = Roo.state.Manager;
34264 var sm = new Roo.LayoutStateManager();
34265 sm.init(this, provider);
34271 * Adds a xtype elements to the layout.
34275 xtype : 'ContentPanel',
34282 xtype : 'NestedLayoutPanel',
34288 items : [ ... list of content panels or nested layout panels.. ]
34292 * @param {Object} cfg Xtype definition of item to add.
34294 addxtype : function(cfg)
34296 // basically accepts a pannel...
34297 // can accept a layout region..!?!?
34298 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34301 // theory? children can only be panels??
34303 //if (!cfg.xtype.match(/Panel$/)) {
34308 if (typeof(cfg.region) == 'undefined') {
34309 Roo.log("Failed to add Panel, region was not set");
34313 var region = cfg.region;
34319 xitems = cfg.items;
34326 case 'Content': // ContentPanel (el, cfg)
34327 case 'Scroll': // ContentPanel (el, cfg)
34329 cfg.autoCreate = true;
34330 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34332 // var el = this.el.createChild();
34333 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34336 this.add(region, ret);
34340 case 'TreePanel': // our new panel!
34341 cfg.el = this.el.createChild();
34342 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34343 this.add(region, ret);
34348 // create a new Layout (which is a Border Layout...
34350 var clayout = cfg.layout;
34351 clayout.el = this.el.createChild();
34352 clayout.items = clayout.items || [];
34356 // replace this exitems with the clayout ones..
34357 xitems = clayout.items;
34359 // force background off if it's in center...
34360 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34361 cfg.background = false;
34363 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
34366 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34367 //console.log('adding nested layout panel ' + cfg.toSource());
34368 this.add(region, ret);
34369 nb = {}; /// find first...
34374 // needs grid and region
34376 //var el = this.getRegion(region).el.createChild();
34378 *var el = this.el.createChild();
34379 // create the grid first...
34380 cfg.grid.container = el;
34381 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
34384 if (region == 'center' && this.active ) {
34385 cfg.background = false;
34388 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34390 this.add(region, ret);
34392 if (cfg.background) {
34393 // render grid on panel activation (if panel background)
34394 ret.on('activate', function(gp) {
34395 if (!gp.grid.rendered) {
34396 // gp.grid.render(el);
34400 // cfg.grid.render(el);
34406 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
34407 // it was the old xcomponent building that caused this before.
34408 // espeically if border is the top element in the tree.
34418 if (typeof(Roo[cfg.xtype]) != 'undefined') {
34420 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34421 this.add(region, ret);
34425 throw "Can not add '" + cfg.xtype + "' to Border";
34431 this.beginUpdate();
34435 Roo.each(xitems, function(i) {
34436 region = nb && i.region ? i.region : false;
34438 var add = ret.addxtype(i);
34441 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
34442 if (!i.background) {
34443 abn[region] = nb[region] ;
34450 // make the last non-background panel active..
34451 //if (nb) { Roo.log(abn); }
34454 for(var r in abn) {
34455 region = this.getRegion(r);
34457 // tried using nb[r], but it does not work..
34459 region.showPanel(abn[r]);
34470 factory : function(cfg)
34473 var validRegions = Roo.bootstrap.layout.Border.regions;
34475 var target = cfg.region;
34478 var r = Roo.bootstrap.layout;
34482 return new r.North(cfg);
34484 return new r.South(cfg);
34486 return new r.East(cfg);
34488 return new r.West(cfg);
34490 return new r.Center(cfg);
34492 throw 'Layout region "'+target+'" not supported.';
34499 * Ext JS Library 1.1.1
34500 * Copyright(c) 2006-2007, Ext JS, LLC.
34502 * Originally Released Under LGPL - original licence link has changed is not relivant.
34505 * <script type="text/javascript">
34509 * @class Roo.bootstrap.layout.Basic
34510 * @extends Roo.util.Observable
34511 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
34512 * and does not have a titlebar, tabs or any other features. All it does is size and position
34513 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
34514 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
34515 * @cfg {string} region the region that it inhabits..
34516 * @cfg {bool} skipConfig skip config?
34520 Roo.bootstrap.layout.Basic = function(config){
34522 this.mgr = config.mgr;
34524 this.position = config.region;
34526 var skipConfig = config.skipConfig;
34530 * @scope Roo.BasicLayoutRegion
34534 * @event beforeremove
34535 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
34536 * @param {Roo.LayoutRegion} this
34537 * @param {Roo.ContentPanel} panel The panel
34538 * @param {Object} e The cancel event object
34540 "beforeremove" : true,
34542 * @event invalidated
34543 * Fires when the layout for this region is changed.
34544 * @param {Roo.LayoutRegion} this
34546 "invalidated" : true,
34548 * @event visibilitychange
34549 * Fires when this region is shown or hidden
34550 * @param {Roo.LayoutRegion} this
34551 * @param {Boolean} visibility true or false
34553 "visibilitychange" : true,
34555 * @event paneladded
34556 * Fires when a panel is added.
34557 * @param {Roo.LayoutRegion} this
34558 * @param {Roo.ContentPanel} panel The panel
34560 "paneladded" : true,
34562 * @event panelremoved
34563 * Fires when a panel is removed.
34564 * @param {Roo.LayoutRegion} this
34565 * @param {Roo.ContentPanel} panel The panel
34567 "panelremoved" : true,
34569 * @event beforecollapse
34570 * Fires when this region before collapse.
34571 * @param {Roo.LayoutRegion} this
34573 "beforecollapse" : true,
34576 * Fires when this region is collapsed.
34577 * @param {Roo.LayoutRegion} this
34579 "collapsed" : true,
34582 * Fires when this region is expanded.
34583 * @param {Roo.LayoutRegion} this
34588 * Fires when this region is slid into view.
34589 * @param {Roo.LayoutRegion} this
34591 "slideshow" : true,
34594 * Fires when this region slides out of view.
34595 * @param {Roo.LayoutRegion} this
34597 "slidehide" : true,
34599 * @event panelactivated
34600 * Fires when a panel is activated.
34601 * @param {Roo.LayoutRegion} this
34602 * @param {Roo.ContentPanel} panel The activated panel
34604 "panelactivated" : true,
34607 * Fires when the user resizes this region.
34608 * @param {Roo.LayoutRegion} this
34609 * @param {Number} newSize The new size (width for east/west, height for north/south)
34613 /** A collection of panels in this region. @type Roo.util.MixedCollection */
34614 this.panels = new Roo.util.MixedCollection();
34615 this.panels.getKey = this.getPanelId.createDelegate(this);
34617 this.activePanel = null;
34618 // ensure listeners are added...
34620 if (config.listeners || config.events) {
34621 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
34622 listeners : config.listeners || {},
34623 events : config.events || {}
34627 if(skipConfig !== true){
34628 this.applyConfig(config);
34632 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
34634 getPanelId : function(p){
34638 applyConfig : function(config){
34639 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
34640 this.config = config;
34645 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
34646 * the width, for horizontal (north, south) the height.
34647 * @param {Number} newSize The new width or height
34649 resizeTo : function(newSize){
34650 var el = this.el ? this.el :
34651 (this.activePanel ? this.activePanel.getEl() : null);
34653 switch(this.position){
34656 el.setWidth(newSize);
34657 this.fireEvent("resized", this, newSize);
34661 el.setHeight(newSize);
34662 this.fireEvent("resized", this, newSize);
34668 getBox : function(){
34669 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
34672 getMargins : function(){
34673 return this.margins;
34676 updateBox : function(box){
34678 var el = this.activePanel.getEl();
34679 el.dom.style.left = box.x + "px";
34680 el.dom.style.top = box.y + "px";
34681 this.activePanel.setSize(box.width, box.height);
34685 * Returns the container element for this region.
34686 * @return {Roo.Element}
34688 getEl : function(){
34689 return this.activePanel;
34693 * Returns true if this region is currently visible.
34694 * @return {Boolean}
34696 isVisible : function(){
34697 return this.activePanel ? true : false;
34700 setActivePanel : function(panel){
34701 panel = this.getPanel(panel);
34702 if(this.activePanel && this.activePanel != panel){
34703 this.activePanel.setActiveState(false);
34704 this.activePanel.getEl().setLeftTop(-10000,-10000);
34706 this.activePanel = panel;
34707 panel.setActiveState(true);
34709 panel.setSize(this.box.width, this.box.height);
34711 this.fireEvent("panelactivated", this, panel);
34712 this.fireEvent("invalidated");
34716 * Show the specified panel.
34717 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
34718 * @return {Roo.ContentPanel} The shown panel or null
34720 showPanel : function(panel){
34721 panel = this.getPanel(panel);
34723 this.setActivePanel(panel);
34729 * Get the active panel for this region.
34730 * @return {Roo.ContentPanel} The active panel or null
34732 getActivePanel : function(){
34733 return this.activePanel;
34737 * Add the passed ContentPanel(s)
34738 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34739 * @return {Roo.ContentPanel} The panel added (if only one was added)
34741 add : function(panel){
34742 if(arguments.length > 1){
34743 for(var i = 0, len = arguments.length; i < len; i++) {
34744 this.add(arguments[i]);
34748 if(this.hasPanel(panel)){
34749 this.showPanel(panel);
34752 var el = panel.getEl();
34753 if(el.dom.parentNode != this.mgr.el.dom){
34754 this.mgr.el.dom.appendChild(el.dom);
34756 if(panel.setRegion){
34757 panel.setRegion(this);
34759 this.panels.add(panel);
34760 el.setStyle("position", "absolute");
34761 if(!panel.background){
34762 this.setActivePanel(panel);
34763 if(this.config.initialSize && this.panels.getCount()==1){
34764 this.resizeTo(this.config.initialSize);
34767 this.fireEvent("paneladded", this, panel);
34772 * Returns true if the panel is in this region.
34773 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34774 * @return {Boolean}
34776 hasPanel : function(panel){
34777 if(typeof panel == "object"){ // must be panel obj
34778 panel = panel.getId();
34780 return this.getPanel(panel) ? true : false;
34784 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34785 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34786 * @param {Boolean} preservePanel Overrides the config preservePanel option
34787 * @return {Roo.ContentPanel} The panel that was removed
34789 remove : function(panel, preservePanel){
34790 panel = this.getPanel(panel);
34795 this.fireEvent("beforeremove", this, panel, e);
34796 if(e.cancel === true){
34799 var panelId = panel.getId();
34800 this.panels.removeKey(panelId);
34805 * Returns the panel specified or null if it's not in this region.
34806 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34807 * @return {Roo.ContentPanel}
34809 getPanel : function(id){
34810 if(typeof id == "object"){ // must be panel obj
34813 return this.panels.get(id);
34817 * Returns this regions position (north/south/east/west/center).
34820 getPosition: function(){
34821 return this.position;
34825 * Ext JS Library 1.1.1
34826 * Copyright(c) 2006-2007, Ext JS, LLC.
34828 * Originally Released Under LGPL - original licence link has changed is not relivant.
34831 * <script type="text/javascript">
34835 * @class Roo.bootstrap.layout.Region
34836 * @extends Roo.bootstrap.layout.Basic
34837 * This class represents a region in a layout manager.
34839 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
34840 * @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})
34841 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
34842 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
34843 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
34844 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
34845 * @cfg {String} title The title for the region (overrides panel titles)
34846 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
34847 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
34848 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
34849 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
34850 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
34851 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
34852 * the space available, similar to FireFox 1.5 tabs (defaults to false)
34853 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
34854 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
34855 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
34857 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
34858 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
34859 * @cfg {Boolean} disableTabTips True to disable tab tooltips
34860 * @cfg {Number} width For East/West panels
34861 * @cfg {Number} height For North/South panels
34862 * @cfg {Boolean} split To show the splitter
34863 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
34865 * @cfg {string} cls Extra CSS classes to add to region
34867 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
34868 * @cfg {string} region the region that it inhabits..
34871 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
34872 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
34874 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
34875 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
34876 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
34878 Roo.bootstrap.layout.Region = function(config)
34880 this.applyConfig(config);
34882 var mgr = config.mgr;
34883 var pos = config.region;
34884 config.skipConfig = true;
34885 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
34888 this.onRender(mgr.el);
34891 this.visible = true;
34892 this.collapsed = false;
34893 this.unrendered_panels = [];
34896 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
34898 position: '', // set by wrapper (eg. north/south etc..)
34899 unrendered_panels : null, // unrendered panels.
34900 createBody : function(){
34901 /** This region's body element
34902 * @type Roo.Element */
34903 this.bodyEl = this.el.createChild({
34905 cls: "roo-layout-panel-body tab-content" // bootstrap added...
34909 onRender: function(ctr, pos)
34911 var dh = Roo.DomHelper;
34912 /** This region's container element
34913 * @type Roo.Element */
34914 this.el = dh.append(ctr.dom, {
34916 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
34918 /** This region's title element
34919 * @type Roo.Element */
34921 this.titleEl = dh.append(this.el.dom,
34924 unselectable: "on",
34925 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
34927 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
34928 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
34931 this.titleEl.enableDisplayMode();
34932 /** This region's title text element
34933 * @type HTMLElement */
34934 this.titleTextEl = this.titleEl.dom.firstChild;
34935 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
34937 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
34938 this.closeBtn.enableDisplayMode();
34939 this.closeBtn.on("click", this.closeClicked, this);
34940 this.closeBtn.hide();
34942 this.createBody(this.config);
34943 if(this.config.hideWhenEmpty){
34945 this.on("paneladded", this.validateVisibility, this);
34946 this.on("panelremoved", this.validateVisibility, this);
34948 if(this.autoScroll){
34949 this.bodyEl.setStyle("overflow", "auto");
34951 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
34953 //if(c.titlebar !== false){
34954 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
34955 this.titleEl.hide();
34957 this.titleEl.show();
34958 if(this.config.title){
34959 this.titleTextEl.innerHTML = this.config.title;
34963 if(this.config.collapsed){
34964 this.collapse(true);
34966 if(this.config.hidden){
34970 if (this.unrendered_panels && this.unrendered_panels.length) {
34971 for (var i =0;i< this.unrendered_panels.length; i++) {
34972 this.add(this.unrendered_panels[i]);
34974 this.unrendered_panels = null;
34980 applyConfig : function(c)
34983 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
34984 var dh = Roo.DomHelper;
34985 if(c.titlebar !== false){
34986 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
34987 this.collapseBtn.on("click", this.collapse, this);
34988 this.collapseBtn.enableDisplayMode();
34990 if(c.showPin === true || this.showPin){
34991 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
34992 this.stickBtn.enableDisplayMode();
34993 this.stickBtn.on("click", this.expand, this);
34994 this.stickBtn.hide();
34999 /** This region's collapsed element
35000 * @type Roo.Element */
35003 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35004 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35007 if(c.floatable !== false){
35008 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35009 this.collapsedEl.on("click", this.collapseClick, this);
35012 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35013 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35014 id: "message", unselectable: "on", style:{"float":"left"}});
35015 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35017 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35018 this.expandBtn.on("click", this.expand, this);
35022 if(this.collapseBtn){
35023 this.collapseBtn.setVisible(c.collapsible == true);
35026 this.cmargins = c.cmargins || this.cmargins ||
35027 (this.position == "west" || this.position == "east" ?
35028 {top: 0, left: 2, right:2, bottom: 0} :
35029 {top: 2, left: 0, right:0, bottom: 2});
35031 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35034 this.bottomTabs = c.tabPosition != "top";
35036 this.autoScroll = c.autoScroll || false;
35041 this.duration = c.duration || .30;
35042 this.slideDuration = c.slideDuration || .45;
35047 * Returns true if this region is currently visible.
35048 * @return {Boolean}
35050 isVisible : function(){
35051 return this.visible;
35055 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35056 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
35058 //setCollapsedTitle : function(title){
35059 // title = title || " ";
35060 // if(this.collapsedTitleTextEl){
35061 // this.collapsedTitleTextEl.innerHTML = title;
35065 getBox : function(){
35067 // if(!this.collapsed){
35068 b = this.el.getBox(false, true);
35070 // b = this.collapsedEl.getBox(false, true);
35075 getMargins : function(){
35076 return this.margins;
35077 //return this.collapsed ? this.cmargins : this.margins;
35080 highlight : function(){
35081 this.el.addClass("x-layout-panel-dragover");
35084 unhighlight : function(){
35085 this.el.removeClass("x-layout-panel-dragover");
35088 updateBox : function(box)
35090 if (!this.bodyEl) {
35091 return; // not rendered yet..
35095 if(!this.collapsed){
35096 this.el.dom.style.left = box.x + "px";
35097 this.el.dom.style.top = box.y + "px";
35098 this.updateBody(box.width, box.height);
35100 this.collapsedEl.dom.style.left = box.x + "px";
35101 this.collapsedEl.dom.style.top = box.y + "px";
35102 this.collapsedEl.setSize(box.width, box.height);
35105 this.tabs.autoSizeTabs();
35109 updateBody : function(w, h)
35112 this.el.setWidth(w);
35113 w -= this.el.getBorderWidth("rl");
35114 if(this.config.adjustments){
35115 w += this.config.adjustments[0];
35118 if(h !== null && h > 0){
35119 this.el.setHeight(h);
35120 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35121 h -= this.el.getBorderWidth("tb");
35122 if(this.config.adjustments){
35123 h += this.config.adjustments[1];
35125 this.bodyEl.setHeight(h);
35127 h = this.tabs.syncHeight(h);
35130 if(this.panelSize){
35131 w = w !== null ? w : this.panelSize.width;
35132 h = h !== null ? h : this.panelSize.height;
35134 if(this.activePanel){
35135 var el = this.activePanel.getEl();
35136 w = w !== null ? w : el.getWidth();
35137 h = h !== null ? h : el.getHeight();
35138 this.panelSize = {width: w, height: h};
35139 this.activePanel.setSize(w, h);
35141 if(Roo.isIE && this.tabs){
35142 this.tabs.el.repaint();
35147 * Returns the container element for this region.
35148 * @return {Roo.Element}
35150 getEl : function(){
35155 * Hides this region.
35158 //if(!this.collapsed){
35159 this.el.dom.style.left = "-2000px";
35162 // this.collapsedEl.dom.style.left = "-2000px";
35163 // this.collapsedEl.hide();
35165 this.visible = false;
35166 this.fireEvent("visibilitychange", this, false);
35170 * Shows this region if it was previously hidden.
35173 //if(!this.collapsed){
35176 // this.collapsedEl.show();
35178 this.visible = true;
35179 this.fireEvent("visibilitychange", this, true);
35182 closeClicked : function(){
35183 if(this.activePanel){
35184 this.remove(this.activePanel);
35188 collapseClick : function(e){
35190 e.stopPropagation();
35193 e.stopPropagation();
35199 * Collapses this region.
35200 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35203 collapse : function(skipAnim, skipCheck = false){
35204 if(this.collapsed) {
35208 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35210 this.collapsed = true;
35212 this.split.el.hide();
35214 if(this.config.animate && skipAnim !== true){
35215 this.fireEvent("invalidated", this);
35216 this.animateCollapse();
35218 this.el.setLocation(-20000,-20000);
35220 this.collapsedEl.show();
35221 this.fireEvent("collapsed", this);
35222 this.fireEvent("invalidated", this);
35228 animateCollapse : function(){
35233 * Expands this region if it was previously collapsed.
35234 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35235 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35238 expand : function(e, skipAnim){
35240 e.stopPropagation();
35242 if(!this.collapsed || this.el.hasActiveFx()) {
35246 this.afterSlideIn();
35249 this.collapsed = false;
35250 if(this.config.animate && skipAnim !== true){
35251 this.animateExpand();
35255 this.split.el.show();
35257 this.collapsedEl.setLocation(-2000,-2000);
35258 this.collapsedEl.hide();
35259 this.fireEvent("invalidated", this);
35260 this.fireEvent("expanded", this);
35264 animateExpand : function(){
35268 initTabs : function()
35270 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35272 var ts = new Roo.bootstrap.panel.Tabs({
35273 el: this.bodyEl.dom,
35274 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35275 disableTooltips: this.config.disableTabTips,
35276 toolbar : this.config.toolbar
35279 if(this.config.hideTabs){
35280 ts.stripWrap.setDisplayed(false);
35283 ts.resizeTabs = this.config.resizeTabs === true;
35284 ts.minTabWidth = this.config.minTabWidth || 40;
35285 ts.maxTabWidth = this.config.maxTabWidth || 250;
35286 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35287 ts.monitorResize = false;
35288 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35289 ts.bodyEl.addClass('roo-layout-tabs-body');
35290 this.panels.each(this.initPanelAsTab, this);
35293 initPanelAsTab : function(panel){
35294 var ti = this.tabs.addTab(
35298 this.config.closeOnTab && panel.isClosable(),
35301 if(panel.tabTip !== undefined){
35302 ti.setTooltip(panel.tabTip);
35304 ti.on("activate", function(){
35305 this.setActivePanel(panel);
35308 if(this.config.closeOnTab){
35309 ti.on("beforeclose", function(t, e){
35311 this.remove(panel);
35315 panel.tabItem = ti;
35320 updatePanelTitle : function(panel, title)
35322 if(this.activePanel == panel){
35323 this.updateTitle(title);
35326 var ti = this.tabs.getTab(panel.getEl().id);
35328 if(panel.tabTip !== undefined){
35329 ti.setTooltip(panel.tabTip);
35334 updateTitle : function(title){
35335 if(this.titleTextEl && !this.config.title){
35336 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
35340 setActivePanel : function(panel)
35342 panel = this.getPanel(panel);
35343 if(this.activePanel && this.activePanel != panel){
35344 this.activePanel.setActiveState(false);
35346 this.activePanel = panel;
35347 panel.setActiveState(true);
35348 if(this.panelSize){
35349 panel.setSize(this.panelSize.width, this.panelSize.height);
35352 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35354 this.updateTitle(panel.getTitle());
35356 this.fireEvent("invalidated", this);
35358 this.fireEvent("panelactivated", this, panel);
35362 * Shows the specified panel.
35363 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35364 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35366 showPanel : function(panel)
35368 panel = this.getPanel(panel);
35371 var tab = this.tabs.getTab(panel.getEl().id);
35372 if(tab.isHidden()){
35373 this.tabs.unhideTab(tab.id);
35377 this.setActivePanel(panel);
35384 * Get the active panel for this region.
35385 * @return {Roo.ContentPanel} The active panel or null
35387 getActivePanel : function(){
35388 return this.activePanel;
35391 validateVisibility : function(){
35392 if(this.panels.getCount() < 1){
35393 this.updateTitle(" ");
35394 this.closeBtn.hide();
35397 if(!this.isVisible()){
35404 * Adds the passed ContentPanel(s) to this region.
35405 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35406 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
35408 add : function(panel)
35410 if(arguments.length > 1){
35411 for(var i = 0, len = arguments.length; i < len; i++) {
35412 this.add(arguments[i]);
35417 // if we have not been rendered yet, then we can not really do much of this..
35418 if (!this.bodyEl) {
35419 this.unrendered_panels.push(panel);
35426 if(this.hasPanel(panel)){
35427 this.showPanel(panel);
35430 panel.setRegion(this);
35431 this.panels.add(panel);
35432 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
35433 // sinle panel - no tab...?? would it not be better to render it with the tabs,
35434 // and hide them... ???
35435 this.bodyEl.dom.appendChild(panel.getEl().dom);
35436 if(panel.background !== true){
35437 this.setActivePanel(panel);
35439 this.fireEvent("paneladded", this, panel);
35446 this.initPanelAsTab(panel);
35450 if(panel.background !== true){
35451 this.tabs.activate(panel.getEl().id);
35453 this.fireEvent("paneladded", this, panel);
35458 * Hides the tab for the specified panel.
35459 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35461 hidePanel : function(panel){
35462 if(this.tabs && (panel = this.getPanel(panel))){
35463 this.tabs.hideTab(panel.getEl().id);
35468 * Unhides the tab for a previously hidden panel.
35469 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35471 unhidePanel : function(panel){
35472 if(this.tabs && (panel = this.getPanel(panel))){
35473 this.tabs.unhideTab(panel.getEl().id);
35477 clearPanels : function(){
35478 while(this.panels.getCount() > 0){
35479 this.remove(this.panels.first());
35484 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35485 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35486 * @param {Boolean} preservePanel Overrides the config preservePanel option
35487 * @return {Roo.ContentPanel} The panel that was removed
35489 remove : function(panel, preservePanel)
35491 panel = this.getPanel(panel);
35496 this.fireEvent("beforeremove", this, panel, e);
35497 if(e.cancel === true){
35500 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
35501 var panelId = panel.getId();
35502 this.panels.removeKey(panelId);
35504 document.body.appendChild(panel.getEl().dom);
35507 this.tabs.removeTab(panel.getEl().id);
35508 }else if (!preservePanel){
35509 this.bodyEl.dom.removeChild(panel.getEl().dom);
35511 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
35512 var p = this.panels.first();
35513 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
35514 tempEl.appendChild(p.getEl().dom);
35515 this.bodyEl.update("");
35516 this.bodyEl.dom.appendChild(p.getEl().dom);
35518 this.updateTitle(p.getTitle());
35520 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
35521 this.setActivePanel(p);
35523 panel.setRegion(null);
35524 if(this.activePanel == panel){
35525 this.activePanel = null;
35527 if(this.config.autoDestroy !== false && preservePanel !== true){
35528 try{panel.destroy();}catch(e){}
35530 this.fireEvent("panelremoved", this, panel);
35535 * Returns the TabPanel component used by this region
35536 * @return {Roo.TabPanel}
35538 getTabs : function(){
35542 createTool : function(parentEl, className){
35543 var btn = Roo.DomHelper.append(parentEl, {
35545 cls: "x-layout-tools-button",
35548 cls: "roo-layout-tools-button-inner " + className,
35552 btn.addClassOnOver("roo-layout-tools-button-over");
35557 * Ext JS Library 1.1.1
35558 * Copyright(c) 2006-2007, Ext JS, LLC.
35560 * Originally Released Under LGPL - original licence link has changed is not relivant.
35563 * <script type="text/javascript">
35569 * @class Roo.SplitLayoutRegion
35570 * @extends Roo.LayoutRegion
35571 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
35573 Roo.bootstrap.layout.Split = function(config){
35574 this.cursor = config.cursor;
35575 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
35578 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
35580 splitTip : "Drag to resize.",
35581 collapsibleSplitTip : "Drag to resize. Double click to hide.",
35582 useSplitTips : false,
35584 applyConfig : function(config){
35585 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
35588 onRender : function(ctr,pos) {
35590 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
35591 if(!this.config.split){
35596 var splitEl = Roo.DomHelper.append(ctr.dom, {
35598 id: this.el.id + "-split",
35599 cls: "roo-layout-split roo-layout-split-"+this.position,
35602 /** The SplitBar for this region
35603 * @type Roo.SplitBar */
35604 // does not exist yet...
35605 Roo.log([this.position, this.orientation]);
35607 this.split = new Roo.bootstrap.SplitBar({
35608 dragElement : splitEl,
35609 resizingElement: this.el,
35610 orientation : this.orientation
35613 this.split.on("moved", this.onSplitMove, this);
35614 this.split.useShim = this.config.useShim === true;
35615 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
35616 if(this.useSplitTips){
35617 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
35619 //if(config.collapsible){
35620 // this.split.el.on("dblclick", this.collapse, this);
35623 if(typeof this.config.minSize != "undefined"){
35624 this.split.minSize = this.config.minSize;
35626 if(typeof this.config.maxSize != "undefined"){
35627 this.split.maxSize = this.config.maxSize;
35629 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
35630 this.hideSplitter();
35635 getHMaxSize : function(){
35636 var cmax = this.config.maxSize || 10000;
35637 var center = this.mgr.getRegion("center");
35638 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
35641 getVMaxSize : function(){
35642 var cmax = this.config.maxSize || 10000;
35643 var center = this.mgr.getRegion("center");
35644 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
35647 onSplitMove : function(split, newSize){
35648 this.fireEvent("resized", this, newSize);
35652 * Returns the {@link Roo.SplitBar} for this region.
35653 * @return {Roo.SplitBar}
35655 getSplitBar : function(){
35660 this.hideSplitter();
35661 Roo.bootstrap.layout.Split.superclass.hide.call(this);
35664 hideSplitter : function(){
35666 this.split.el.setLocation(-2000,-2000);
35667 this.split.el.hide();
35673 this.split.el.show();
35675 Roo.bootstrap.layout.Split.superclass.show.call(this);
35678 beforeSlide: function(){
35679 if(Roo.isGecko){// firefox overflow auto bug workaround
35680 this.bodyEl.clip();
35682 this.tabs.bodyEl.clip();
35684 if(this.activePanel){
35685 this.activePanel.getEl().clip();
35687 if(this.activePanel.beforeSlide){
35688 this.activePanel.beforeSlide();
35694 afterSlide : function(){
35695 if(Roo.isGecko){// firefox overflow auto bug workaround
35696 this.bodyEl.unclip();
35698 this.tabs.bodyEl.unclip();
35700 if(this.activePanel){
35701 this.activePanel.getEl().unclip();
35702 if(this.activePanel.afterSlide){
35703 this.activePanel.afterSlide();
35709 initAutoHide : function(){
35710 if(this.autoHide !== false){
35711 if(!this.autoHideHd){
35712 var st = new Roo.util.DelayedTask(this.slideIn, this);
35713 this.autoHideHd = {
35714 "mouseout": function(e){
35715 if(!e.within(this.el, true)){
35719 "mouseover" : function(e){
35725 this.el.on(this.autoHideHd);
35729 clearAutoHide : function(){
35730 if(this.autoHide !== false){
35731 this.el.un("mouseout", this.autoHideHd.mouseout);
35732 this.el.un("mouseover", this.autoHideHd.mouseover);
35736 clearMonitor : function(){
35737 Roo.get(document).un("click", this.slideInIf, this);
35740 // these names are backwards but not changed for compat
35741 slideOut : function(){
35742 if(this.isSlid || this.el.hasActiveFx()){
35745 this.isSlid = true;
35746 if(this.collapseBtn){
35747 this.collapseBtn.hide();
35749 this.closeBtnState = this.closeBtn.getStyle('display');
35750 this.closeBtn.hide();
35752 this.stickBtn.show();
35755 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
35756 this.beforeSlide();
35757 this.el.setStyle("z-index", 10001);
35758 this.el.slideIn(this.getSlideAnchor(), {
35759 callback: function(){
35761 this.initAutoHide();
35762 Roo.get(document).on("click", this.slideInIf, this);
35763 this.fireEvent("slideshow", this);
35770 afterSlideIn : function(){
35771 this.clearAutoHide();
35772 this.isSlid = false;
35773 this.clearMonitor();
35774 this.el.setStyle("z-index", "");
35775 if(this.collapseBtn){
35776 this.collapseBtn.show();
35778 this.closeBtn.setStyle('display', this.closeBtnState);
35780 this.stickBtn.hide();
35782 this.fireEvent("slidehide", this);
35785 slideIn : function(cb){
35786 if(!this.isSlid || this.el.hasActiveFx()){
35790 this.isSlid = false;
35791 this.beforeSlide();
35792 this.el.slideOut(this.getSlideAnchor(), {
35793 callback: function(){
35794 this.el.setLeftTop(-10000, -10000);
35796 this.afterSlideIn();
35804 slideInIf : function(e){
35805 if(!e.within(this.el)){
35810 animateCollapse : function(){
35811 this.beforeSlide();
35812 this.el.setStyle("z-index", 20000);
35813 var anchor = this.getSlideAnchor();
35814 this.el.slideOut(anchor, {
35815 callback : function(){
35816 this.el.setStyle("z-index", "");
35817 this.collapsedEl.slideIn(anchor, {duration:.3});
35819 this.el.setLocation(-10000,-10000);
35821 this.fireEvent("collapsed", this);
35828 animateExpand : function(){
35829 this.beforeSlide();
35830 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
35831 this.el.setStyle("z-index", 20000);
35832 this.collapsedEl.hide({
35835 this.el.slideIn(this.getSlideAnchor(), {
35836 callback : function(){
35837 this.el.setStyle("z-index", "");
35840 this.split.el.show();
35842 this.fireEvent("invalidated", this);
35843 this.fireEvent("expanded", this);
35871 getAnchor : function(){
35872 return this.anchors[this.position];
35875 getCollapseAnchor : function(){
35876 return this.canchors[this.position];
35879 getSlideAnchor : function(){
35880 return this.sanchors[this.position];
35883 getAlignAdj : function(){
35884 var cm = this.cmargins;
35885 switch(this.position){
35901 getExpandAdj : function(){
35902 var c = this.collapsedEl, cm = this.cmargins;
35903 switch(this.position){
35905 return [-(cm.right+c.getWidth()+cm.left), 0];
35908 return [cm.right+c.getWidth()+cm.left, 0];
35911 return [0, -(cm.top+cm.bottom+c.getHeight())];
35914 return [0, cm.top+cm.bottom+c.getHeight()];
35920 * Ext JS Library 1.1.1
35921 * Copyright(c) 2006-2007, Ext JS, LLC.
35923 * Originally Released Under LGPL - original licence link has changed is not relivant.
35926 * <script type="text/javascript">
35929 * These classes are private internal classes
35931 Roo.bootstrap.layout.Center = function(config){
35932 config.region = "center";
35933 Roo.bootstrap.layout.Region.call(this, config);
35934 this.visible = true;
35935 this.minWidth = config.minWidth || 20;
35936 this.minHeight = config.minHeight || 20;
35939 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
35941 // center panel can't be hidden
35945 // center panel can't be hidden
35948 getMinWidth: function(){
35949 return this.minWidth;
35952 getMinHeight: function(){
35953 return this.minHeight;
35966 Roo.bootstrap.layout.North = function(config)
35968 config.region = 'north';
35969 config.cursor = 'n-resize';
35971 Roo.bootstrap.layout.Split.call(this, config);
35975 this.split.placement = Roo.bootstrap.SplitBar.TOP;
35976 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
35977 this.split.el.addClass("roo-layout-split-v");
35979 var size = config.initialSize || config.height;
35980 if(typeof size != "undefined"){
35981 this.el.setHeight(size);
35984 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
35986 orientation: Roo.bootstrap.SplitBar.VERTICAL,
35990 getBox : function(){
35991 if(this.collapsed){
35992 return this.collapsedEl.getBox();
35994 var box = this.el.getBox();
35996 box.height += this.split.el.getHeight();
36001 updateBox : function(box){
36002 if(this.split && !this.collapsed){
36003 box.height -= this.split.el.getHeight();
36004 this.split.el.setLeft(box.x);
36005 this.split.el.setTop(box.y+box.height);
36006 this.split.el.setWidth(box.width);
36008 if(this.collapsed){
36009 this.updateBody(box.width, null);
36011 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36019 Roo.bootstrap.layout.South = function(config){
36020 config.region = 'south';
36021 config.cursor = 's-resize';
36022 Roo.bootstrap.layout.Split.call(this, config);
36024 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36025 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36026 this.split.el.addClass("roo-layout-split-v");
36028 var size = config.initialSize || config.height;
36029 if(typeof size != "undefined"){
36030 this.el.setHeight(size);
36034 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36035 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36036 getBox : function(){
36037 if(this.collapsed){
36038 return this.collapsedEl.getBox();
36040 var box = this.el.getBox();
36042 var sh = this.split.el.getHeight();
36049 updateBox : function(box){
36050 if(this.split && !this.collapsed){
36051 var sh = this.split.el.getHeight();
36054 this.split.el.setLeft(box.x);
36055 this.split.el.setTop(box.y-sh);
36056 this.split.el.setWidth(box.width);
36058 if(this.collapsed){
36059 this.updateBody(box.width, null);
36061 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36065 Roo.bootstrap.layout.East = function(config){
36066 config.region = "east";
36067 config.cursor = "e-resize";
36068 Roo.bootstrap.layout.Split.call(this, config);
36070 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36071 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36072 this.split.el.addClass("roo-layout-split-h");
36074 var size = config.initialSize || config.width;
36075 if(typeof size != "undefined"){
36076 this.el.setWidth(size);
36079 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36080 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36081 getBox : function(){
36082 if(this.collapsed){
36083 return this.collapsedEl.getBox();
36085 var box = this.el.getBox();
36087 var sw = this.split.el.getWidth();
36094 updateBox : function(box){
36095 if(this.split && !this.collapsed){
36096 var sw = this.split.el.getWidth();
36098 this.split.el.setLeft(box.x);
36099 this.split.el.setTop(box.y);
36100 this.split.el.setHeight(box.height);
36103 if(this.collapsed){
36104 this.updateBody(null, box.height);
36106 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36110 Roo.bootstrap.layout.West = function(config){
36111 config.region = "west";
36112 config.cursor = "w-resize";
36114 Roo.bootstrap.layout.Split.call(this, config);
36116 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36117 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36118 this.split.el.addClass("roo-layout-split-h");
36122 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36123 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36125 onRender: function(ctr, pos)
36127 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36128 var size = this.config.initialSize || this.config.width;
36129 if(typeof size != "undefined"){
36130 this.el.setWidth(size);
36134 getBox : function(){
36135 if(this.collapsed){
36136 return this.collapsedEl.getBox();
36138 var box = this.el.getBox();
36140 box.width += this.split.el.getWidth();
36145 updateBox : function(box){
36146 if(this.split && !this.collapsed){
36147 var sw = this.split.el.getWidth();
36149 this.split.el.setLeft(box.x+box.width);
36150 this.split.el.setTop(box.y);
36151 this.split.el.setHeight(box.height);
36153 if(this.collapsed){
36154 this.updateBody(null, box.height);
36156 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36159 Roo.namespace("Roo.bootstrap.panel");/*
36161 * Ext JS Library 1.1.1
36162 * Copyright(c) 2006-2007, Ext JS, LLC.
36164 * Originally Released Under LGPL - original licence link has changed is not relivant.
36167 * <script type="text/javascript">
36170 * @class Roo.ContentPanel
36171 * @extends Roo.util.Observable
36172 * A basic ContentPanel element.
36173 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
36174 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
36175 * @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
36176 * @cfg {Boolean} closable True if the panel can be closed/removed
36177 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
36178 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36179 * @cfg {Toolbar} toolbar A toolbar for this panel
36180 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
36181 * @cfg {String} title The title for this panel
36182 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36183 * @cfg {String} url Calls {@link #setUrl} with this value
36184 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36185 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
36186 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
36187 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
36188 * @cfg {Boolean} badges render the badges
36191 * Create a new ContentPanel.
36192 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36193 * @param {String/Object} config A string to set only the title or a config object
36194 * @param {String} content (optional) Set the HTML content for this panel
36195 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36197 Roo.bootstrap.panel.Content = function( config){
36199 this.tpl = config.tpl || false;
36201 var el = config.el;
36202 var content = config.content;
36204 if(config.autoCreate){ // xtype is available if this is called from factory
36207 this.el = Roo.get(el);
36208 if(!this.el && config && config.autoCreate){
36209 if(typeof config.autoCreate == "object"){
36210 if(!config.autoCreate.id){
36211 config.autoCreate.id = config.id||el;
36213 this.el = Roo.DomHelper.append(document.body,
36214 config.autoCreate, true);
36216 var elcfg = { tag: "div",
36217 cls: "roo-layout-inactive-content",
36221 elcfg.html = config.html;
36225 this.el = Roo.DomHelper.append(document.body, elcfg , true);
36228 this.closable = false;
36229 this.loaded = false;
36230 this.active = false;
36233 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36235 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36237 this.wrapEl = this.el; //this.el.wrap();
36239 if (config.toolbar.items) {
36240 ti = config.toolbar.items ;
36241 delete config.toolbar.items ;
36245 this.toolbar.render(this.wrapEl, 'before');
36246 for(var i =0;i < ti.length;i++) {
36247 // Roo.log(['add child', items[i]]);
36248 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36250 this.toolbar.items = nitems;
36251 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36252 delete config.toolbar;
36256 // xtype created footer. - not sure if will work as we normally have to render first..
36257 if (this.footer && !this.footer.el && this.footer.xtype) {
36258 if (!this.wrapEl) {
36259 this.wrapEl = this.el.wrap();
36262 this.footer.container = this.wrapEl.createChild();
36264 this.footer = Roo.factory(this.footer, Roo);
36269 if(typeof config == "string"){
36270 this.title = config;
36272 Roo.apply(this, config);
36276 this.resizeEl = Roo.get(this.resizeEl, true);
36278 this.resizeEl = this.el;
36280 // handle view.xtype
36288 * Fires when this panel is activated.
36289 * @param {Roo.ContentPanel} this
36293 * @event deactivate
36294 * Fires when this panel is activated.
36295 * @param {Roo.ContentPanel} this
36297 "deactivate" : true,
36301 * Fires when this panel is resized if fitToFrame is true.
36302 * @param {Roo.ContentPanel} this
36303 * @param {Number} width The width after any component adjustments
36304 * @param {Number} height The height after any component adjustments
36310 * Fires when this tab is created
36311 * @param {Roo.ContentPanel} this
36322 if(this.autoScroll){
36323 this.resizeEl.setStyle("overflow", "auto");
36325 // fix randome scrolling
36326 //this.el.on('scroll', function() {
36327 // Roo.log('fix random scolling');
36328 // this.scrollTo('top',0);
36331 content = content || this.content;
36333 this.setContent(content);
36335 if(config && config.url){
36336 this.setUrl(this.url, this.params, this.loadOnce);
36341 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36343 if (this.view && typeof(this.view.xtype) != 'undefined') {
36344 this.view.el = this.el.appendChild(document.createElement("div"));
36345 this.view = Roo.factory(this.view);
36346 this.view.render && this.view.render(false, '');
36350 this.fireEvent('render', this);
36353 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36357 setRegion : function(region){
36358 this.region = region;
36359 this.setActiveClass(region && !this.background);
36363 setActiveClass: function(state)
36366 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36367 this.el.setStyle('position','relative');
36369 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36370 this.el.setStyle('position', 'absolute');
36375 * Returns the toolbar for this Panel if one was configured.
36376 * @return {Roo.Toolbar}
36378 getToolbar : function(){
36379 return this.toolbar;
36382 setActiveState : function(active)
36384 this.active = active;
36385 this.setActiveClass(active);
36387 this.fireEvent("deactivate", this);
36389 this.fireEvent("activate", this);
36393 * Updates this panel's element
36394 * @param {String} content The new content
36395 * @param {Boolean} loadScripts (optional) true to look for and process scripts
36397 setContent : function(content, loadScripts){
36398 this.el.update(content, loadScripts);
36401 ignoreResize : function(w, h){
36402 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
36405 this.lastSize = {width: w, height: h};
36410 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
36411 * @return {Roo.UpdateManager} The UpdateManager
36413 getUpdateManager : function(){
36414 return this.el.getUpdateManager();
36417 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
36418 * @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:
36421 url: "your-url.php",
36422 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
36423 callback: yourFunction,
36424 scope: yourObject, //(optional scope)
36427 text: "Loading...",
36432 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
36433 * 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.
36434 * @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}
36435 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
36436 * @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.
36437 * @return {Roo.ContentPanel} this
36440 var um = this.el.getUpdateManager();
36441 um.update.apply(um, arguments);
36447 * 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.
36448 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
36449 * @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)
36450 * @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)
36451 * @return {Roo.UpdateManager} The UpdateManager
36453 setUrl : function(url, params, loadOnce){
36454 if(this.refreshDelegate){
36455 this.removeListener("activate", this.refreshDelegate);
36457 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
36458 this.on("activate", this.refreshDelegate);
36459 return this.el.getUpdateManager();
36462 _handleRefresh : function(url, params, loadOnce){
36463 if(!loadOnce || !this.loaded){
36464 var updater = this.el.getUpdateManager();
36465 updater.update(url, params, this._setLoaded.createDelegate(this));
36469 _setLoaded : function(){
36470 this.loaded = true;
36474 * Returns this panel's id
36477 getId : function(){
36482 * Returns this panel's element - used by regiosn to add.
36483 * @return {Roo.Element}
36485 getEl : function(){
36486 return this.wrapEl || this.el;
36491 adjustForComponents : function(width, height)
36493 //Roo.log('adjustForComponents ');
36494 if(this.resizeEl != this.el){
36495 width -= this.el.getFrameWidth('lr');
36496 height -= this.el.getFrameWidth('tb');
36499 var te = this.toolbar.getEl();
36500 te.setWidth(width);
36501 height -= te.getHeight();
36504 var te = this.footer.getEl();
36505 te.setWidth(width);
36506 height -= te.getHeight();
36510 if(this.adjustments){
36511 width += this.adjustments[0];
36512 height += this.adjustments[1];
36514 return {"width": width, "height": height};
36517 setSize : function(width, height){
36518 if(this.fitToFrame && !this.ignoreResize(width, height)){
36519 if(this.fitContainer && this.resizeEl != this.el){
36520 this.el.setSize(width, height);
36522 var size = this.adjustForComponents(width, height);
36523 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
36524 this.fireEvent('resize', this, size.width, size.height);
36529 * Returns this panel's title
36532 getTitle : function(){
36534 if (typeof(this.title) != 'object') {
36539 for (var k in this.title) {
36540 if (!this.title.hasOwnProperty(k)) {
36544 if (k.indexOf('-') >= 0) {
36545 var s = k.split('-');
36546 for (var i = 0; i<s.length; i++) {
36547 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
36550 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
36557 * Set this panel's title
36558 * @param {String} title
36560 setTitle : function(title){
36561 this.title = title;
36563 this.region.updatePanelTitle(this, title);
36568 * Returns true is this panel was configured to be closable
36569 * @return {Boolean}
36571 isClosable : function(){
36572 return this.closable;
36575 beforeSlide : function(){
36577 this.resizeEl.clip();
36580 afterSlide : function(){
36582 this.resizeEl.unclip();
36586 * Force a content refresh from the URL specified in the {@link #setUrl} method.
36587 * Will fail silently if the {@link #setUrl} method has not been called.
36588 * This does not activate the panel, just updates its content.
36590 refresh : function(){
36591 if(this.refreshDelegate){
36592 this.loaded = false;
36593 this.refreshDelegate();
36598 * Destroys this panel
36600 destroy : function(){
36601 this.el.removeAllListeners();
36602 var tempEl = document.createElement("span");
36603 tempEl.appendChild(this.el.dom);
36604 tempEl.innerHTML = "";
36610 * form - if the content panel contains a form - this is a reference to it.
36611 * @type {Roo.form.Form}
36615 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
36616 * This contains a reference to it.
36622 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
36632 * @param {Object} cfg Xtype definition of item to add.
36636 getChildContainer: function () {
36637 return this.getEl();
36642 var ret = new Roo.factory(cfg);
36647 if (cfg.xtype.match(/^Form$/)) {
36650 //if (this.footer) {
36651 // el = this.footer.container.insertSibling(false, 'before');
36653 el = this.el.createChild();
36656 this.form = new Roo.form.Form(cfg);
36659 if ( this.form.allItems.length) {
36660 this.form.render(el.dom);
36664 // should only have one of theses..
36665 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
36666 // views.. should not be just added - used named prop 'view''
36668 cfg.el = this.el.appendChild(document.createElement("div"));
36671 var ret = new Roo.factory(cfg);
36673 ret.render && ret.render(false, ''); // render blank..
36683 * @class Roo.bootstrap.panel.Grid
36684 * @extends Roo.bootstrap.panel.Content
36686 * Create a new GridPanel.
36687 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
36688 * @param {Object} config A the config object
36694 Roo.bootstrap.panel.Grid = function(config)
36698 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
36699 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
36701 config.el = this.wrapper;
36702 //this.el = this.wrapper;
36704 if (config.container) {
36705 // ctor'ed from a Border/panel.grid
36708 this.wrapper.setStyle("overflow", "hidden");
36709 this.wrapper.addClass('roo-grid-container');
36714 if(config.toolbar){
36715 var tool_el = this.wrapper.createChild();
36716 this.toolbar = Roo.factory(config.toolbar);
36718 if (config.toolbar.items) {
36719 ti = config.toolbar.items ;
36720 delete config.toolbar.items ;
36724 this.toolbar.render(tool_el);
36725 for(var i =0;i < ti.length;i++) {
36726 // Roo.log(['add child', items[i]]);
36727 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36729 this.toolbar.items = nitems;
36731 delete config.toolbar;
36734 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
36735 config.grid.scrollBody = true;;
36736 config.grid.monitorWindowResize = false; // turn off autosizing
36737 config.grid.autoHeight = false;
36738 config.grid.autoWidth = false;
36740 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
36742 if (config.background) {
36743 // render grid on panel activation (if panel background)
36744 this.on('activate', function(gp) {
36745 if (!gp.grid.rendered) {
36746 gp.grid.render(this.wrapper);
36747 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
36752 this.grid.render(this.wrapper);
36753 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
36756 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
36757 // ??? needed ??? config.el = this.wrapper;
36762 // xtype created footer. - not sure if will work as we normally have to render first..
36763 if (this.footer && !this.footer.el && this.footer.xtype) {
36765 var ctr = this.grid.getView().getFooterPanel(true);
36766 this.footer.dataSource = this.grid.dataSource;
36767 this.footer = Roo.factory(this.footer, Roo);
36768 this.footer.render(ctr);
36778 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
36779 getId : function(){
36780 return this.grid.id;
36784 * Returns the grid for this panel
36785 * @return {Roo.bootstrap.Table}
36787 getGrid : function(){
36791 setSize : function(width, height){
36792 if(!this.ignoreResize(width, height)){
36793 var grid = this.grid;
36794 var size = this.adjustForComponents(width, height);
36795 var gridel = grid.getGridEl();
36796 gridel.setSize(size.width, size.height);
36798 var thd = grid.getGridEl().select('thead',true).first();
36799 var tbd = grid.getGridEl().select('tbody', true).first();
36801 tbd.setSize(width, height - thd.getHeight());
36810 beforeSlide : function(){
36811 this.grid.getView().scroller.clip();
36814 afterSlide : function(){
36815 this.grid.getView().scroller.unclip();
36818 destroy : function(){
36819 this.grid.destroy();
36821 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
36826 * @class Roo.bootstrap.panel.Nest
36827 * @extends Roo.bootstrap.panel.Content
36829 * Create a new Panel, that can contain a layout.Border.
36832 * @param {Roo.BorderLayout} layout The layout for this panel
36833 * @param {String/Object} config A string to set only the title or a config object
36835 Roo.bootstrap.panel.Nest = function(config)
36837 // construct with only one argument..
36838 /* FIXME - implement nicer consturctors
36839 if (layout.layout) {
36841 layout = config.layout;
36842 delete config.layout;
36844 if (layout.xtype && !layout.getEl) {
36845 // then layout needs constructing..
36846 layout = Roo.factory(layout, Roo);
36850 config.el = config.layout.getEl();
36852 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
36854 config.layout.monitorWindowResize = false; // turn off autosizing
36855 this.layout = config.layout;
36856 this.layout.getEl().addClass("roo-layout-nested-layout");
36863 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
36865 setSize : function(width, height){
36866 if(!this.ignoreResize(width, height)){
36867 var size = this.adjustForComponents(width, height);
36868 var el = this.layout.getEl();
36869 if (size.height < 1) {
36870 el.setWidth(size.width);
36872 el.setSize(size.width, size.height);
36874 var touch = el.dom.offsetWidth;
36875 this.layout.layout();
36876 // ie requires a double layout on the first pass
36877 if(Roo.isIE && !this.initialized){
36878 this.initialized = true;
36879 this.layout.layout();
36884 // activate all subpanels if not currently active..
36886 setActiveState : function(active){
36887 this.active = active;
36888 this.setActiveClass(active);
36891 this.fireEvent("deactivate", this);
36895 this.fireEvent("activate", this);
36896 // not sure if this should happen before or after..
36897 if (!this.layout) {
36898 return; // should not happen..
36901 for (var r in this.layout.regions) {
36902 reg = this.layout.getRegion(r);
36903 if (reg.getActivePanel()) {
36904 //reg.showPanel(reg.getActivePanel()); // force it to activate..
36905 reg.setActivePanel(reg.getActivePanel());
36908 if (!reg.panels.length) {
36911 reg.showPanel(reg.getPanel(0));
36920 * Returns the nested BorderLayout for this panel
36921 * @return {Roo.BorderLayout}
36923 getLayout : function(){
36924 return this.layout;
36928 * Adds a xtype elements to the layout of the nested panel
36932 xtype : 'ContentPanel',
36939 xtype : 'NestedLayoutPanel',
36945 items : [ ... list of content panels or nested layout panels.. ]
36949 * @param {Object} cfg Xtype definition of item to add.
36951 addxtype : function(cfg) {
36952 return this.layout.addxtype(cfg);
36957 * Ext JS Library 1.1.1
36958 * Copyright(c) 2006-2007, Ext JS, LLC.
36960 * Originally Released Under LGPL - original licence link has changed is not relivant.
36963 * <script type="text/javascript">
36966 * @class Roo.TabPanel
36967 * @extends Roo.util.Observable
36968 * A lightweight tab container.
36972 // basic tabs 1, built from existing content
36973 var tabs = new Roo.TabPanel("tabs1");
36974 tabs.addTab("script", "View Script");
36975 tabs.addTab("markup", "View Markup");
36976 tabs.activate("script");
36978 // more advanced tabs, built from javascript
36979 var jtabs = new Roo.TabPanel("jtabs");
36980 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
36982 // set up the UpdateManager
36983 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
36984 var updater = tab2.getUpdateManager();
36985 updater.setDefaultUrl("ajax1.htm");
36986 tab2.on('activate', updater.refresh, updater, true);
36988 // Use setUrl for Ajax loading
36989 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
36990 tab3.setUrl("ajax2.htm", null, true);
36993 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
36996 jtabs.activate("jtabs-1");
36999 * Create a new TabPanel.
37000 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37001 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37003 Roo.bootstrap.panel.Tabs = function(config){
37005 * The container element for this TabPanel.
37006 * @type Roo.Element
37008 this.el = Roo.get(config.el);
37011 if(typeof config == "boolean"){
37012 this.tabPosition = config ? "bottom" : "top";
37014 Roo.apply(this, config);
37018 if(this.tabPosition == "bottom"){
37019 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37020 this.el.addClass("roo-tabs-bottom");
37022 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37023 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37024 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37026 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37028 if(this.tabPosition != "bottom"){
37029 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37030 * @type Roo.Element
37032 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37033 this.el.addClass("roo-tabs-top");
37037 this.bodyEl.setStyle("position", "relative");
37039 this.active = null;
37040 this.activateDelegate = this.activate.createDelegate(this);
37045 * Fires when the active tab changes
37046 * @param {Roo.TabPanel} this
37047 * @param {Roo.TabPanelItem} activePanel The new active tab
37051 * @event beforetabchange
37052 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37053 * @param {Roo.TabPanel} this
37054 * @param {Object} e Set cancel to true on this object to cancel the tab change
37055 * @param {Roo.TabPanelItem} tab The tab being changed to
37057 "beforetabchange" : true
37060 Roo.EventManager.onWindowResize(this.onResize, this);
37061 this.cpad = this.el.getPadding("lr");
37062 this.hiddenCount = 0;
37065 // toolbar on the tabbar support...
37066 if (this.toolbar) {
37067 alert("no toolbar support yet");
37068 this.toolbar = false;
37070 var tcfg = this.toolbar;
37071 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
37072 this.toolbar = new Roo.Toolbar(tcfg);
37073 if (Roo.isSafari) {
37074 var tbl = tcfg.container.child('table', true);
37075 tbl.setAttribute('width', '100%');
37083 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37086 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37088 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37090 tabPosition : "top",
37092 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37094 currentTabWidth : 0,
37096 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37100 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37104 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37106 preferredTabWidth : 175,
37108 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37110 resizeTabs : false,
37112 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37114 monitorResize : true,
37116 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
37121 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37122 * @param {String} id The id of the div to use <b>or create</b>
37123 * @param {String} text The text for the tab
37124 * @param {String} content (optional) Content to put in the TabPanelItem body
37125 * @param {Boolean} closable (optional) True to create a close icon on the tab
37126 * @return {Roo.TabPanelItem} The created TabPanelItem
37128 addTab : function(id, text, content, closable, tpl)
37130 var item = new Roo.bootstrap.panel.TabItem({
37134 closable : closable,
37137 this.addTabItem(item);
37139 item.setContent(content);
37145 * Returns the {@link Roo.TabPanelItem} with the specified id/index
37146 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37147 * @return {Roo.TabPanelItem}
37149 getTab : function(id){
37150 return this.items[id];
37154 * Hides the {@link Roo.TabPanelItem} with the specified id/index
37155 * @param {String/Number} id The id or index of the TabPanelItem to hide.
37157 hideTab : function(id){
37158 var t = this.items[id];
37161 this.hiddenCount++;
37162 this.autoSizeTabs();
37167 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37168 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37170 unhideTab : function(id){
37171 var t = this.items[id];
37173 t.setHidden(false);
37174 this.hiddenCount--;
37175 this.autoSizeTabs();
37180 * Adds an existing {@link Roo.TabPanelItem}.
37181 * @param {Roo.TabPanelItem} item The TabPanelItem to add
37183 addTabItem : function(item){
37184 this.items[item.id] = item;
37185 this.items.push(item);
37186 // if(this.resizeTabs){
37187 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37188 // this.autoSizeTabs();
37190 // item.autoSize();
37195 * Removes a {@link Roo.TabPanelItem}.
37196 * @param {String/Number} id The id or index of the TabPanelItem to remove.
37198 removeTab : function(id){
37199 var items = this.items;
37200 var tab = items[id];
37201 if(!tab) { return; }
37202 var index = items.indexOf(tab);
37203 if(this.active == tab && items.length > 1){
37204 var newTab = this.getNextAvailable(index);
37209 this.stripEl.dom.removeChild(tab.pnode.dom);
37210 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37211 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37213 items.splice(index, 1);
37214 delete this.items[tab.id];
37215 tab.fireEvent("close", tab);
37216 tab.purgeListeners();
37217 this.autoSizeTabs();
37220 getNextAvailable : function(start){
37221 var items = this.items;
37223 // look for a next tab that will slide over to
37224 // replace the one being removed
37225 while(index < items.length){
37226 var item = items[++index];
37227 if(item && !item.isHidden()){
37231 // if one isn't found select the previous tab (on the left)
37234 var item = items[--index];
37235 if(item && !item.isHidden()){
37243 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37244 * @param {String/Number} id The id or index of the TabPanelItem to disable.
37246 disableTab : function(id){
37247 var tab = this.items[id];
37248 if(tab && this.active != tab){
37254 * Enables a {@link Roo.TabPanelItem} that is disabled.
37255 * @param {String/Number} id The id or index of the TabPanelItem to enable.
37257 enableTab : function(id){
37258 var tab = this.items[id];
37263 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37264 * @param {String/Number} id The id or index of the TabPanelItem to activate.
37265 * @return {Roo.TabPanelItem} The TabPanelItem.
37267 activate : function(id){
37268 var tab = this.items[id];
37272 if(tab == this.active || tab.disabled){
37276 this.fireEvent("beforetabchange", this, e, tab);
37277 if(e.cancel !== true && !tab.disabled){
37279 this.active.hide();
37281 this.active = this.items[id];
37282 this.active.show();
37283 this.fireEvent("tabchange", this, this.active);
37289 * Gets the active {@link Roo.TabPanelItem}.
37290 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37292 getActiveTab : function(){
37293 return this.active;
37297 * Updates the tab body element to fit the height of the container element
37298 * for overflow scrolling
37299 * @param {Number} targetHeight (optional) Override the starting height from the elements height
37301 syncHeight : function(targetHeight){
37302 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37303 var bm = this.bodyEl.getMargins();
37304 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37305 this.bodyEl.setHeight(newHeight);
37309 onResize : function(){
37310 if(this.monitorResize){
37311 this.autoSizeTabs();
37316 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37318 beginUpdate : function(){
37319 this.updating = true;
37323 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37325 endUpdate : function(){
37326 this.updating = false;
37327 this.autoSizeTabs();
37331 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37333 autoSizeTabs : function(){
37334 var count = this.items.length;
37335 var vcount = count - this.hiddenCount;
37336 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37339 var w = Math.max(this.el.getWidth() - this.cpad, 10);
37340 var availWidth = Math.floor(w / vcount);
37341 var b = this.stripBody;
37342 if(b.getWidth() > w){
37343 var tabs = this.items;
37344 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37345 if(availWidth < this.minTabWidth){
37346 /*if(!this.sleft){ // incomplete scrolling code
37347 this.createScrollButtons();
37350 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37353 if(this.currentTabWidth < this.preferredTabWidth){
37354 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37360 * Returns the number of tabs in this TabPanel.
37363 getCount : function(){
37364 return this.items.length;
37368 * Resizes all the tabs to the passed width
37369 * @param {Number} The new width
37371 setTabWidth : function(width){
37372 this.currentTabWidth = width;
37373 for(var i = 0, len = this.items.length; i < len; i++) {
37374 if(!this.items[i].isHidden()) {
37375 this.items[i].setWidth(width);
37381 * Destroys this TabPanel
37382 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
37384 destroy : function(removeEl){
37385 Roo.EventManager.removeResizeListener(this.onResize, this);
37386 for(var i = 0, len = this.items.length; i < len; i++){
37387 this.items[i].purgeListeners();
37389 if(removeEl === true){
37390 this.el.update("");
37395 createStrip : function(container)
37397 var strip = document.createElement("nav");
37398 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
37399 container.appendChild(strip);
37403 createStripList : function(strip)
37405 // div wrapper for retard IE
37406 // returns the "tr" element.
37407 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
37408 //'<div class="x-tabs-strip-wrap">'+
37409 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
37410 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
37411 return strip.firstChild; //.firstChild.firstChild.firstChild;
37413 createBody : function(container)
37415 var body = document.createElement("div");
37416 Roo.id(body, "tab-body");
37417 //Roo.fly(body).addClass("x-tabs-body");
37418 Roo.fly(body).addClass("tab-content");
37419 container.appendChild(body);
37422 createItemBody :function(bodyEl, id){
37423 var body = Roo.getDom(id);
37425 body = document.createElement("div");
37428 //Roo.fly(body).addClass("x-tabs-item-body");
37429 Roo.fly(body).addClass("tab-pane");
37430 bodyEl.insertBefore(body, bodyEl.firstChild);
37434 createStripElements : function(stripEl, text, closable, tpl)
37436 var td = document.createElement("li"); // was td..
37439 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
37442 stripEl.appendChild(td);
37444 td.className = "x-tabs-closable";
37445 if(!this.closeTpl){
37446 this.closeTpl = new Roo.Template(
37447 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37448 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
37449 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
37452 var el = this.closeTpl.overwrite(td, {"text": text});
37453 var close = el.getElementsByTagName("div")[0];
37454 var inner = el.getElementsByTagName("em")[0];
37455 return {"el": el, "close": close, "inner": inner};
37458 // not sure what this is..
37459 // if(!this.tabTpl){
37460 //this.tabTpl = new Roo.Template(
37461 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37462 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
37464 // this.tabTpl = new Roo.Template(
37465 // '<a href="#">' +
37466 // '<span unselectable="on"' +
37467 // (this.disableTooltips ? '' : ' title="{text}"') +
37468 // ' >{text}</span></a>'
37474 var template = tpl || this.tabTpl || false;
37478 template = new Roo.Template(
37480 '<span unselectable="on"' +
37481 (this.disableTooltips ? '' : ' title="{text}"') +
37482 ' >{text}</span></a>'
37486 switch (typeof(template)) {
37490 template = new Roo.Template(template);
37496 var el = template.overwrite(td, {"text": text});
37498 var inner = el.getElementsByTagName("span")[0];
37500 return {"el": el, "inner": inner};
37508 * @class Roo.TabPanelItem
37509 * @extends Roo.util.Observable
37510 * Represents an individual item (tab plus body) in a TabPanel.
37511 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
37512 * @param {String} id The id of this TabPanelItem
37513 * @param {String} text The text for the tab of this TabPanelItem
37514 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
37516 Roo.bootstrap.panel.TabItem = function(config){
37518 * The {@link Roo.TabPanel} this TabPanelItem belongs to
37519 * @type Roo.TabPanel
37521 this.tabPanel = config.panel;
37523 * The id for this TabPanelItem
37526 this.id = config.id;
37528 this.disabled = false;
37530 this.text = config.text;
37532 this.loaded = false;
37533 this.closable = config.closable;
37536 * The body element for this TabPanelItem.
37537 * @type Roo.Element
37539 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
37540 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
37541 this.bodyEl.setStyle("display", "block");
37542 this.bodyEl.setStyle("zoom", "1");
37543 //this.hideAction();
37545 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
37547 this.el = Roo.get(els.el);
37548 this.inner = Roo.get(els.inner, true);
37549 this.textEl = Roo.get(this.el.dom.firstChild, true);
37550 this.pnode = Roo.get(els.el.parentNode, true);
37551 this.el.on("mousedown", this.onTabMouseDown, this);
37552 this.el.on("click", this.onTabClick, this);
37554 if(config.closable){
37555 var c = Roo.get(els.close, true);
37556 c.dom.title = this.closeText;
37557 c.addClassOnOver("close-over");
37558 c.on("click", this.closeClick, this);
37564 * Fires when this tab becomes the active tab.
37565 * @param {Roo.TabPanel} tabPanel The parent TabPanel
37566 * @param {Roo.TabPanelItem} this
37570 * @event beforeclose
37571 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
37572 * @param {Roo.TabPanelItem} this
37573 * @param {Object} e Set cancel to true on this object to cancel the close.
37575 "beforeclose": true,
37578 * Fires when this tab is closed.
37579 * @param {Roo.TabPanelItem} this
37583 * @event deactivate
37584 * Fires when this tab is no longer the active tab.
37585 * @param {Roo.TabPanel} tabPanel The parent TabPanel
37586 * @param {Roo.TabPanelItem} this
37588 "deactivate" : true
37590 this.hidden = false;
37592 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
37595 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
37597 purgeListeners : function(){
37598 Roo.util.Observable.prototype.purgeListeners.call(this);
37599 this.el.removeAllListeners();
37602 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
37605 this.pnode.addClass("active");
37608 this.tabPanel.stripWrap.repaint();
37610 this.fireEvent("activate", this.tabPanel, this);
37614 * Returns true if this tab is the active tab.
37615 * @return {Boolean}
37617 isActive : function(){
37618 return this.tabPanel.getActiveTab() == this;
37622 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
37625 this.pnode.removeClass("active");
37627 this.fireEvent("deactivate", this.tabPanel, this);
37630 hideAction : function(){
37631 this.bodyEl.hide();
37632 this.bodyEl.setStyle("position", "absolute");
37633 this.bodyEl.setLeft("-20000px");
37634 this.bodyEl.setTop("-20000px");
37637 showAction : function(){
37638 this.bodyEl.setStyle("position", "relative");
37639 this.bodyEl.setTop("");
37640 this.bodyEl.setLeft("");
37641 this.bodyEl.show();
37645 * Set the tooltip for the tab.
37646 * @param {String} tooltip The tab's tooltip
37648 setTooltip : function(text){
37649 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
37650 this.textEl.dom.qtip = text;
37651 this.textEl.dom.removeAttribute('title');
37653 this.textEl.dom.title = text;
37657 onTabClick : function(e){
37658 e.preventDefault();
37659 this.tabPanel.activate(this.id);
37662 onTabMouseDown : function(e){
37663 e.preventDefault();
37664 this.tabPanel.activate(this.id);
37667 getWidth : function(){
37668 return this.inner.getWidth();
37671 setWidth : function(width){
37672 var iwidth = width - this.pnode.getPadding("lr");
37673 this.inner.setWidth(iwidth);
37674 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
37675 this.pnode.setWidth(width);
37679 * Show or hide the tab
37680 * @param {Boolean} hidden True to hide or false to show.
37682 setHidden : function(hidden){
37683 this.hidden = hidden;
37684 this.pnode.setStyle("display", hidden ? "none" : "");
37688 * Returns true if this tab is "hidden"
37689 * @return {Boolean}
37691 isHidden : function(){
37692 return this.hidden;
37696 * Returns the text for this tab
37699 getText : function(){
37703 autoSize : function(){
37704 //this.el.beginMeasure();
37705 this.textEl.setWidth(1);
37707 * #2804 [new] Tabs in Roojs
37708 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
37710 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
37711 //this.el.endMeasure();
37715 * Sets the text for the tab (Note: this also sets the tooltip text)
37716 * @param {String} text The tab's text and tooltip
37718 setText : function(text){
37720 this.textEl.update(text);
37721 this.setTooltip(text);
37722 //if(!this.tabPanel.resizeTabs){
37723 // this.autoSize();
37727 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
37729 activate : function(){
37730 this.tabPanel.activate(this.id);
37734 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
37736 disable : function(){
37737 if(this.tabPanel.active != this){
37738 this.disabled = true;
37739 this.pnode.addClass("disabled");
37744 * Enables this TabPanelItem if it was previously disabled.
37746 enable : function(){
37747 this.disabled = false;
37748 this.pnode.removeClass("disabled");
37752 * Sets the content for this TabPanelItem.
37753 * @param {String} content The content
37754 * @param {Boolean} loadScripts true to look for and load scripts
37756 setContent : function(content, loadScripts){
37757 this.bodyEl.update(content, loadScripts);
37761 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
37762 * @return {Roo.UpdateManager} The UpdateManager
37764 getUpdateManager : function(){
37765 return this.bodyEl.getUpdateManager();
37769 * Set a URL to be used to load the content for this TabPanelItem.
37770 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
37771 * @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)
37772 * @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)
37773 * @return {Roo.UpdateManager} The UpdateManager
37775 setUrl : function(url, params, loadOnce){
37776 if(this.refreshDelegate){
37777 this.un('activate', this.refreshDelegate);
37779 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37780 this.on("activate", this.refreshDelegate);
37781 return this.bodyEl.getUpdateManager();
37785 _handleRefresh : function(url, params, loadOnce){
37786 if(!loadOnce || !this.loaded){
37787 var updater = this.bodyEl.getUpdateManager();
37788 updater.update(url, params, this._setLoaded.createDelegate(this));
37793 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
37794 * Will fail silently if the setUrl method has not been called.
37795 * This does not activate the panel, just updates its content.
37797 refresh : function(){
37798 if(this.refreshDelegate){
37799 this.loaded = false;
37800 this.refreshDelegate();
37805 _setLoaded : function(){
37806 this.loaded = true;
37810 closeClick : function(e){
37813 this.fireEvent("beforeclose", this, o);
37814 if(o.cancel !== true){
37815 this.tabPanel.removeTab(this.id);
37819 * The text displayed in the tooltip for the close icon.
37822 closeText : "Close this tab"